How To Contribute¶
As the library follows a modular structure, it is fairly easy to contribute by working on a subpackage and submit a pull request of those atomic changes.
For this, first you should check this repo’s issues and check which modules are being worked on by filtering by the
in-progress label. Then, you can check turf.js github repo, choose a module
that you’d like to implement, making sure that it hasn’t been already implemented or is currently being worked by
When you’re ready, open a new issue on this repo outlining the module you’ll be working on, so that subsequent contributors can follow the same logic outlined here.
Please attend to the following guidelines:
Open an issue in
diogomatoschaves/pyturfoutlining your plan.
Always include tests. pytest is used in this project.
pyturfmodules are small, containing a single exported function. See below for a typical module structure.
Export your module function by including it in
turf/__init__.py. See below for details.
GeoJSON is the lingua franca of
pyturf. It should be used as the data structure for anything that can be represented as geography.
Keep your commits atomic and each of them passing all checks (linter and tests).
Add your new module under the
Add your new module under its appropriate section in docs/source/modules.
Avoid extra dependencies if you can.
Run the linter before submitting changes (see below for more details).
python -m pytest --verbose --cov=./from the project root folder to run all the tests and make sure they pass with a sufficiently high coverage.
Rebase your branch with the upstream master before opening the PR:
git rebase upstream/master
After you open the PR, make sure that the CI pipeline passes all checks (on the
Checks tab of the PR).
To ensure consistent code style, black is used in this project. At the root level run:
$ black .
This will automatically reformat all files according to
Structure of a
For a new module named
new_module, the following structure should be adhered to.
turf | ├── ... ├── __init__.py ├── new_module ├── __init__.py ├── _new_module.py | ├── tests ├── test_new_module.py ├── in │ ├── points.geojson | ├── ... │ ├── out ├── points.geojson ├── ...
Importing modules in
In order for the module function to be imported directly from
turf, we need to import them on the
on both the module and at the root level. So for example, for a new module named
turf/new_module/__init__.py we would include:
from turf.new_module._new_module import new_module
The same logic can be applied to
... from turf.new_module import new_module ...
Tests setup in this project follows a certain pattern that, even if not being a one size fits all, if followed should ensure a good test flow in most cases.
The pattern consists of importing the tests input and output through files in
turf/new_module/tests/out respectively, and then parameterizing these fixtures to be used in individual tests as required.
These guidelines should be followed:
The file where tests are executed should be under the directory
testsshould have sub directories
out, where input and output files should be kept respectively.
Files in both
outfor a specific test must have the same name, although they can have different file extensions (eg:
Fixtures and expected outputs can then be imported by means of the function
get_fixtures defined in
turf/utils/test_setup.py, by providing the test file path as input:
import os from turf.utils.test_setup import get_fixtures current_path = os.path.dirname(os.path.realpath(__file__)) fixtures = get_fixtures(current_path)
The returned value
fixtures becomes a dictionary of fixtures, with the file names in
out as keys,
and in turn each fixture is a dictionary containing keys
"out", representing the input and
output of the tests respectively.
If for some reason you only have either
out fixtures, then in order to avoid errors running the tests
you should pass the argument
get_fixtures as shown below:
import os from turf.utils.test_setup import get_fixtures current_path = os.path.dirname(os.path.realpath(__file__)) fixtures = get_fixtures(current_path, keys=["in"]) # Only retrieve input fixtures
These fixtures can then be parameterized as individual tests, allowing for only one test definition to be used in multiple test cases. This would follow a structure of the kind:
import pytest from turf.new_module import new_module # Don't import your function directly from turf @pytest.mark.parametrize( "fixture", [ pytest.param(fixture, id=fixture_name) for fixture_name, fixture in fixtures.items() ], ) def test_new_module(self, fixture): # This is an example function call assert new_module(fixture["in"]) == fixture["out"]
In order to run the tests, from the root directory run:
$ python -m pytest --verbose --cov=./
Updating The Documentation¶
In case you add a new module, please update also the documentation. The structure
for the documentation follows the
turf structure. You can check turf.js documentation.
According to the
turf.js documentation, you can pick the same
block name and update it with your addition.
turf | ├── ... ├── __init__.py ├── docs ├── ... ├── conf.py | ├── modules ├── aggregation.rst ├── assertion.rst ├── booleans.rst ├── ...
As an example, for adding the
length module you would have to add the following lines to
Length ------ .. autofunction:: turf.length