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: Better documentation for developers #427

Merged
merged 29 commits into from
Jul 10, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
3e118f1
Use semantic linefeeds in tests section
zzril Jul 6, 2023
1a89244
Add developer documentation about testcases
zzril Jul 6, 2023
deafb7b
Improve wording in tests section
zzril Jul 6, 2023
23639d1
Add section about not working in-place
zzril Jul 6, 2023
8eb4f82
Shorten test example
zzril Jul 6, 2023
1ad1db1
Add docstring example for non-in-place methods
zzril Jul 6, 2023
6a837dd
Remove empty example box
zzril Jul 6, 2023
adeefcb
Add "code style" section to guidelines
zzril Jul 6, 2023
874987a
Merge branch 'main' into 375-better-documentation-for-developers
zzril Jul 7, 2023
63347fd
Merge branch 'main' into 375-better-documentation-for-developers
zzril Jul 8, 2023
c255f8d
Add code review guidelines
zzril Jul 9, 2023
8a4b831
Add entry to mkdocs.yml
zzril Jul 9, 2023
5500c34
Fix html links and typos
zzril Jul 9, 2023
8cfdcd7
Improve wording in check pr section
zzril Jul 9, 2023
12d3d1c
Use markdown cross references
zzril Jul 9, 2023
d939431
Use hardcoded links
zzril Jul 9, 2023
ae83188
Use normal text for the links
zzril Jul 9, 2023
734392a
Fix typo in code style section
zzril Jul 9, 2023
3f7f894
Add final paragraph to code review guide
zzril Jul 9, 2023
7793432
Remove duplicated remark about __all__ lists
zzril Jul 9, 2023
09c0c9a
Merge branch 'main' into 375-better-documentation-for-developers
zzril Jul 9, 2023
e34ce7e
Apply suggestions from code review
zzril Jul 10, 2023
db0267d
Resolve issues mentioned in code review
zzril Jul 10, 2023
576abde
Resolve issues mentioned in code review
zzril Jul 10, 2023
642543b
Remove dead link temporarily
zzril Jul 10, 2023
93053e1
Apply renaming suggestions from code review
zzril Jul 10, 2023
81767ee
Restructure test section
zzril Jul 10, 2023
d3d6010
Update docs/development/code_review.md
lars-reimann Jul 10, 2023
2e96f51
docs: rename document to "Project Guidelines"
lars-reimann Jul 10, 2023
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
81 changes: 81 additions & 0 deletions docs/development/code_review.md
lars-reimann marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Code review

This document describes
general guidelines for developers
when reviewing a Pull Request (PR).
lars-reimann marked this conversation as resolved.
Show resolved Hide resolved
zzril marked this conversation as resolved.
Show resolved Hide resolved

## Verify that the PR solves the addressed issue

### Understand the issue

* Read the issue that was addressed by the PR and make sure you understand it
* Read any discussions that occurred on the issue page

### Check the Pull Request

* Check the Pull Request for __completeness__: Was every point from the issue covered?
* Check for __correctness__: Were the problems described in the issue solved in the intended way?
* (For PRs that introduce new features:) Check the design - does it comply with our [guidelines][guidelines-general]?
zzril marked this conversation as resolved.
Show resolved Hide resolved
* Check for potential bugs - edge cases, exceptions that may be raised in functions that were called etc.
* Check the code style: Is it readable? Do variables have sensible names? Is it consistent to existing code? Is there a better / more elegant way to do certain things (e.g. f-string instead of manual string concatenation)?
lars-reimann marked this conversation as resolved.
Show resolved Hide resolved
* Check any warnings reported by your IDE

## Check the tests

* Run the tests locally
* Check if there are any warnings that are not caught by the test suite
* Make sure the test cases are complete - do they cover all edge cases (e.g. empty tables)?
* Make sure they comply to our [guidelines][guidelines-tests] - are the tests parametrized and do all testcases have descriptive IDs?
zzril marked this conversation as resolved.
Show resolved Hide resolved

## Verify that the PR does not break existing code

This is largely covered by the automated tests.
However, you should always:

* Make sure all tests actually ran through (pytest, linter, code coverage)
* Make sure that the branch is up-to-date with the `main` branch, so you actually test the behaviour that will result once the feature branch is merged

## Check the Pull Request format

* Check that the PR title starts with a fitting [type](https://github.com/Safe-DS/.github/blob/main/.github/CONTRIBUTING.md#types) (e.g. `feat`, `fix`, ...)
zzril marked this conversation as resolved.
Show resolved Hide resolved
* Check that the changes introduced by the PR are documented in the PR message
* Check that the `time spent by team` has been updated on the issue page
lars-reimann marked this conversation as resolved.
Show resolved Hide resolved

## Requesting changes

If you found any issues with the reviewed PR,
navigate to the `Files changed` tab in the PR page
and click on the respective line
to add your comments.

For more details on specific review features,
check out the [documentation on GitHub][github-review].

## Finishing the review

When done, finish your review
by navigating to the `Files changed` tab
and clicking the green `Review changes` button
on the top right.

If you found no issues,
select the `Approve` option
to approve the changes made by the PR.
If you like, you can add a "LGTM" meme
from [lgtm.party](https://lgtm.party/),
preferably one with a cat image.

If no other reviewer suggested changes either,
please also mark the corresponding issue
as `Ready to merge` on the issue's page
to update its status on the issue board.
lars-reimann marked this conversation as resolved.
Show resolved Hide resolved

If you found any problems,
select `Request Changes` instead.
In this case, please also
mark the corresponding issue
as `In progress` again.
lars-reimann marked this conversation as resolved.
Show resolved Hide resolved

[guidelines-general]: https://stdlib.safeds.com/en/stable/development/guidelines/
[guidelines-tests]: https://stdlib.safeds.com/en/stable/development/guidelines/#tests
lars-reimann marked this conversation as resolved.
Show resolved Hide resolved
[github-review]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests
139 changes: 117 additions & 22 deletions docs/development/guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,43 @@ Some flag parameters drastically alter the semantics of a function. This can lea
table.drop("name", axis="columns")
```

### Return copies of objects

Modifying objects in-place
can lead to surprising behaviour
jxnior01 marked this conversation as resolved.
Show resolved Hide resolved
and hard-to-find bugs.
Methods shall never change
the object they're called on
or any of their parameters.

!!! success "**DO** (library code):"

```py
result = self._data.copy()
result.columns = self._schema.column_names
result[new_column.name] = new_column._data
return Table._from_pandas_dataframe(result)
```

!!! failure "**DON'T** (library code):"

```py
self._data.add(new_column, axis='column')
```

The corresponding docstring should explicitly state
that a method returns a copy:

!!! success "**DO** (library code):"

```py
"""
Return a new table with the given column added as the last column.
The original table is not modified.
...
"""
```

### Avoid uncommon abbreviations

Write full words rather than abbreviations. The increased verbosity is offset by better readability, better functioning auto-completion, and a reduced need to consult the documentation when writing code. Common abbreviations like CSV or HTML are fine though, since they rarely require explanation.
Expand Down Expand Up @@ -205,27 +242,6 @@ The user should not have to deal with exceptions that are defined in the wrapper
return pd.read_csv(path) # May raise a pd.ParserError
```

### Sort entries in `__all__` lists alphabetically
The entries in the `__all__` list in `__init__.py` files should be sorted alphabetically. This helps reduce the likelihood of merge conflicts when new entries are introduced on different branches.
!!! success "**DO** (library code):"

```py
__all__ = [
"ColumnSizeError",
"DuplicateColumnNameError",
"MissingValuesColumnError"
]
```
!!! failure "**DON'T** (library code):"

```py
__all__ = [
"MissingValuesColumnError",
"ColumnSizeError",
"DuplicateColumnNameError"
]
```

### Group API elements by task

Packages should correspond to a specific task like classification or imputation. This eases discovery and makes it easy to switch between different solutions for the same task.
Expand Down Expand Up @@ -258,6 +274,49 @@ Passing values that are commonly used together around separately is tedious, ver
training_feature_vectors, validation_feature_vectors, training_target_values, validation_target_values = split(feature_vectors, target_values)
```

## Code style
lars-reimann marked this conversation as resolved.
Show resolved Hide resolved

### 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** (library code):"

```py
__all__ = [
"Column",
"Row",
"Table",
"TaggedTable",
zzril marked this conversation as resolved.
Show resolved Hide resolved
]
```

!!! failure "**DON'T** (library code):"

```py
__all__ = [
"Table",
"TaggedTable",
"Column",
"Row",
zzril marked this conversation as resolved.
Show resolved Hide resolved
]
```

## 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.
Expand Down Expand Up @@ -370,7 +429,43 @@ Examples

## Tests
lars-reimann marked this conversation as resolved.
Show resolved Hide resolved

If a function contains more code than just the getting or setting of a value, automated test should be added to the [`tests`][tests-folder] folder. The file structure in the tests folder should mirror the file structure of the [`src`][src-folder] folder.
If a function contains more code
than just the getting or setting of a value,
automated test should be added
to the [`tests`][tests-folder] folder.
lars-reimann marked this conversation as resolved.
Show resolved Hide resolved
The file structure in the tests folder
should mirror the file structure
of the [`src`][src-folder] folder.

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

### Testcases
zzril marked this conversation as resolved.
Show resolved Hide resolved

Tests should be parametrized
using `@pytest.mark.parametrize`,
even if there is only a single testcase.
lars-reimann marked this conversation as resolved.
Show resolved Hide resolved
zzril marked this conversation as resolved.
Show resolved Hide resolved
Testcases should be given
zzril marked this conversation as resolved.
Show resolved Hide resolved
descriptive IDs.

Example:

```py
@pytest.mark.parametrize(
("path", "expected"),
[
("table.csv", Table({"A": [1], "B": [2]})),
(Path("table.csv"), Table({"A": [1], "B": [2]})),
("empty_table.csv", Table()),
],
ids=["by string", "by path", "empty"],
)
def test_should_create_table_from_csv_file(path: str | Path, expected: Table) -> None:
...
```
lars-reimann marked this conversation as resolved.
Show resolved Hide resolved

[src-folder]: https://github.com/Safe-DS/Stdlib/tree/main/src
[tests-folder]: https://github.com/Safe-DS/Stdlib/tree/main/tests
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ nav:
- Environment: development/environment.md
- Guidelines: development/guidelines.md
lars-reimann marked this conversation as resolved.
Show resolved Hide resolved
- Contributing: https://github.com/Safe-DS/Stdlib/contribute
- Code Review Guide: development/code_review.md

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

Expand Down