|
| 1 | +How to Contribute Code |
| 2 | +====================== |
| 3 | + |
| 4 | +This document describes how the BO4E Python implementation is written and what to watch out for. |
| 5 | + |
| 6 | +Technical Setup in your IDE |
| 7 | +=========================== |
| 8 | + |
| 9 | +We're using tox. Please follow the instructions in our `Python Template Repository <https://github.com/Hochfrequenz/python_template_repository#how-to-use-this-repository-on-your-machine>`_. Feel free to open an issue if you run into any kind of problems. |
| 10 | + |
| 11 | +Coding Style and Guidelines |
| 12 | +=========================== |
| 13 | + |
| 14 | +General Rules |
| 15 | +------------- |
| 16 | + |
| 17 | +- We use (and enforce in the CI): |
| 18 | + - black for formatting |
| 19 | + - pylint for linting |
| 20 | + - mypy for static type checking |
| 21 | + - pytest for unittests |
| 22 | + - Sphinx and Plantuml (and kroki web service) for documentation |
| 23 | +- Technical Documentation is in English; For example: "Don't use the builtin validator here because β¦" |
| 24 | +- But data model docstrings are in German; For example: "Ist das Ende nicht gesetzt, so ist der Zeitraum als offen zu verstehen." |
| 25 | +- Docstrings should not be trivial/useless |
| 26 | + - Bad: "Energiemenge ist eine Klasse zur Abbildung von Energiemengen." β (no shit Sherlock) |
| 27 | + - Good: "Eine Energiemenge ordnet einer :class:`Marktlokation` oder :class:`Messlokation`, die ΓΌber die `lokations_id` referenziert werden, einen oder mehrere EnergieverbrΓ€uche zu." β |
| 28 | +- Only sentences have a full stop at the end. |
| 29 | +- We use ``snake_case`` internally but serialize as ``camelCase`` by overriding the ``data_key`` property of the schema fields. |
| 30 | + |
| 31 | +How to Define an ENUM? |
| 32 | +---------------------- |
| 33 | + |
| 34 | +All Enums inherit from ``bo4e.enum.StrEnum``. The latter is just a usual Enum with a ``str`` mixin (see `the official docs <https://docs.python.org/3/library/enum.html?highlight=strenum#others>`_ for details). This allows us to precisely define how an enum value will be serialized. All enum values have UPPER_CASE names. |
| 35 | + |
| 36 | +.. code-block:: python |
| 37 | +
|
| 38 | + # pylint:disable=missing-module-docstring |
| 39 | +
|
| 40 | + from bo4e.enum.strenum import StrEnum |
| 41 | +
|
| 42 | + class MyBo4eEnum(StrEnum): |
| 43 | + """ |
| 44 | + Hier sollte ein deutscher Text stehen, der Sinn und Zweck des Enums beschreibt. |
| 45 | + """ |
| 46 | +
|
| 47 | + FOO = "FOO" #: FOO ist, wenn der Himmel blau ist |
| 48 | + BAR = "BAR" |
| 49 | + """ |
| 50 | + Der Docstring fΓΌr BAR kann auch hier drunter stehen, wenn er besonders lang ist und mehr sagen will, |
| 51 | + als dass BAR fΓΌr die grΓΌne Wiese steht. Denn manchmal braucht es mehr als hundert Zeichen. |
| 52 | + """ |
| 53 | + EIGENSCHAFT_0815 = "0815" #: manchmal heiΓen eigenschaften anders (EIGENSCHAFT_0815) als sie serialisiert werden ("0815") |
| 54 | + # this typically happens for annoying enum values that contains "-" or start with digits |
| 55 | +
|
| 56 | +How to Define COMs or BOs |
| 57 | +------------------------- |
| 58 | + |
| 59 | +All COMponents inherit from ``bo4e.com.com.COM``. All Business Objects inherit from ``bo4e.bo.geschaeftsobjekt.Geschaeftsobjekt``. |
| 60 | + |
| 61 | +For data validation and de/serialization we use `pydantic <https://pydantic-docs.helpmanual.io/>`_. |
| 62 | + |
| 63 | +.. code-block:: python |
| 64 | +
|
| 65 | + """ |
| 66 | + Give the module a docstring to describe what it contains |
| 67 | + """ |
| 68 | +
|
| 69 | + from pydantic import validator |
| 70 | +
|
| 71 | + from datetime import datetime |
| 72 | + from typing import Optional, Dict, Any |
| 73 | +
|
| 74 | + from ..bo.geschaeftsobjekt import Geschaeftsobjekt |
| 75 | + from ..com.menge import Menge |
| 76 | + from ..enum.typ import BoTyp |
| 77 | +
|
| 78 | + # pylint: disable=too-few-public-methods |
| 79 | + class MeinBo(Geschaeftsobjekt): |
| 80 | + """ |
| 81 | + MeinBo ist ein ganz besonderes Business Objekt. |
| 82 | + Es kommt nur bei meinem Strom-Lieferanten zum Einsatz und beschreibt dort all die tollen Eigenschaften, die mein Verbrauchsverhalten hat. |
| 83 | + """ |
| 84 | +
|
| 85 | + typ: Annotated[Optional[Typ], Field(alias="_typ")] = Typ.MEINBO |
| 86 | +
|
| 87 | + #: Der Lieferbeginn beschreibt den Zeitpunkt ab dem (inklusiv) mich ein Versorger seinen Kunden nennen darf |
| 88 | + lieferbeginn: Optional[datetime] = None |
| 89 | +
|
| 90 | + anzahl_freudenspruenge: Optional[int] = None |
| 91 | + """ |
| 92 | + Anzahl FreudensprΓΌnge beschreibt, wie oft der CEO des Stromkonzerns in die Luft gesprungen ist, als ich den Vertrag unterschrieben habe. |
| 93 | + """ |
| 94 | +
|
| 95 | + #: Menge (Elektrische Energie oder Gas oder WΓ€rme), die ich zum Lieferbeginn umsonst erhalte |
| 96 | + freimenge: Optional[Menge] = None |
| 97 | +
|
| 98 | + # we can help you with anything you might be missing or unable to implement. |
| 99 | + # ToDo comments are just fine. |
| 100 | + # You don't need to be a perfect programmer to contribute to bo4e :) |
| 101 | +
|
| 102 | +Unittests |
| 103 | +--------- |
| 104 | + |
| 105 | +Ideally provide unittests that show: |
| 106 | + |
| 107 | +- that the BO/COM can be instantiated |
| 108 | + - with only the required attributes |
| 109 | + - with all attributes |
| 110 | +- can be serialized and deserialized again |
| 111 | + - with only the required attributes |
| 112 | + - with all attributes |
| 113 | + |
| 114 | +Therefore, copy one of the existing "roundtrip" tests, see f.e. ``TestTarifeinschraenkung``. |
| 115 | + |
| 116 | +Pull Request |
| 117 | +============ |
| 118 | + |
| 119 | +Open a Pull Request against the main/default branch of this repository. We'd appreciate if you allowed maintainer edits. |
| 120 | + |
| 121 | +Release Workflow |
| 122 | +================ |
| 123 | + |
| 124 | +- Check with tox all tests and linting: ``tox`` |
| 125 | +- Check with tox if the packaging works fine: ``tox -e test_packaging`` |
| 126 | +- Squash Merge all your changes you would like to have in the release into the main/default branch |
| 127 | +- Check that all GitHub Actions for tests and linting do pass (should be automatically enforced for PRs against main) |
| 128 | +- Go to the repositorys right sidebar and click on `Draft a new release <https://github.com/Hochfrequenz/BO4E-python/releases/new>`_ |
| 129 | +- Write in the *Tag version* field and in the *Release title* your new version, i.e. ``v0.0.6`` |
| 130 | +- Add a description to the release (or just autogenerate the change log which will be fine for 95% of cases) |
| 131 | +- Publish the release |
| 132 | + |
| 133 | +There is a GitHub Action which gets triggered by a release event. It will run all default tests with tox. If they pass, it will take the tag title to replace the version information in the *setup.cfg* file. After checking the package with ``twine check`` it will finally upload the new package release. |
0 commit comments