diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000000000000000000000000000000000000..2534a45ee555bb5ebefd37210d19399634c7a4ee
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,3 @@
+[flake8]
+max-line-length = 80
+ignore = E501,W503,E302,E402,E203
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..6352dfc534c96dd6ea1c8bc7c061f4745b199881
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,27 @@
+# See https://pre-commit.com for more information
+# See https://pre-commit.com/hooks.html for more hooks
+repos:
+  - repo: https://github.com/timothycrosley/isort
+    rev: 5.10.1
+    hooks:
+      - id: isort
+        args: [--settings-path, "pyproject.toml"]
+  - repo: https://github.com/psf/black
+    rev: 22.3.0
+    hooks:
+      - id: black
+  - repo: https://gitlab.com/pycqa/flake8
+    rev: 3.9.2
+    hooks:
+      - id: flake8
+  - repo: https://github.com/pre-commit/pre-commit-hooks
+    rev: v4.2.0
+    hooks:
+      - id: check-ast
+      - id: check-case-conflict
+      - id: trailing-whitespace
+      - id: end-of-file-fixer
+      - id: debug-statements
+      - id: check-added-large-files
+      - id: check-yaml
+        exclude: .*/meta.yaml
diff --git a/LICENSE b/LICENSE
index bd46ce15068f2d3b5a1b23ac6c68a33ec808d95d..0c3160331a4198b350981b4eb39a1b2f851813f6 100644
--- a/LICENSE
+++ b/LICENSE
@@ -24,4 +24,4 @@ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/bob/__init__.py b/bob/__init__.py
index 2ab1e28b150f0549def9963e9e87de3fdd6b2579..edbb4090fca046b19d22d3982711084621bff3be 100644
--- a/bob/__init__.py
+++ b/bob/__init__.py
@@ -1,3 +1,4 @@
 # see https://docs.python.org/3/library/pkgutil.html
 from pkgutil import extend_path
+
 __path__ = extend_path(__path__, __name__)
diff --git a/bob/io/base/__init__.py b/bob/io/base/__init__.py
index 80612baaf14c78a1d646b7d88b5ca65c25a3a6a1..3e4da95af2edb4f75239fe5f0239911091a39090 100644
--- a/bob/io/base/__init__.py
+++ b/bob/io/base/__init__.py
@@ -1,12 +1,12 @@
 # import Libraries of other lib packages
-import numpy as np
+import logging
+
 import h5py
 import imageio
+import numpy as np
 
 from ..image import to_bob, to_matplotlib
 
-import logging
-
 logger = logging.getLogger(__name__)
 import os
 
@@ -182,6 +182,7 @@ def load(inputs):
     """
 
     from collections.abc import Iterable
+
     import numpy
 
     if _is_string(inputs):
@@ -248,44 +249,6 @@ read = load
 # open = File
 
 
-def get_include_directories():
-    """get_include_directories() -> includes
-
-    Returns a list of include directories for dependent libraries, such as HDF5.
-    This function is automatically used by
-    :py:func:`bob.extension.get_bob_libraries` to retrieve the non-standard
-    include directories that are required to use the C bindings of this library
-    in dependent classes. You shouldn't normally need to call this function by
-    hand.
-
-    **Returns:**
-
-    ``includes`` : [str]
-      The list of non-standard include directories required to use the C bindings
-      of this class. For now, only the directory for the HDF5 headers are
-      returned.
-    """
-    # try to use pkg_config first
-    try:
-        from bob.extension.utils import find_header
-
-        # locate pkg-config on our own
-        header = "hdf5.h"
-        candidates = find_header(header)
-        if not candidates:
-            raise RuntimeError(
-                "could not find %s's `%s' - have you installed %s on this "
-                "machine?" % ("hdf5", header, "hdf5")
-            )
-
-        return [os.path.dirname(candidates[0])]
-    except RuntimeError:
-        from bob.extension import pkgconfig
-
-        pkg = pkgconfig("hdf5")
-        return pkg.include_directories()
-
-
 def _generate_features(reader, paths, same_size=False):
     """Load and stack features in a memory efficient way. This function is
     meant to be used inside :py:func:`vstack_features`.
diff --git a/bob/io/base/test/test_hdf5.py b/bob/io/base/test/test_hdf5.py
index dde0a53e7cb1be00d17734265de6fbdb6b575c30..ec0e148ce0500e23765b76577919f6280bc11a98 100644
--- a/bob/io/base/test/test_hdf5.py
+++ b/bob/io/base/test/test_hdf5.py
@@ -8,12 +8,13 @@
 """
 
 import os
-from bob.io.base import load, save
-from ..test_utils import temporary_filename
-
+import random
 
 import numpy as np
-import random
+
+from bob.io.base import load, save
+
+from ..test_utils import temporary_filename
 
 
 def read_write_check(data):
diff --git a/bob/io/base/test/test_image_support.py b/bob/io/base/test/test_image_support.py
index 08989569b2876163210e5069807a2895d360a1ae..04e1721581f0e535e1f9c4ded8a201de8c9644e7 100644
--- a/bob/io/base/test/test_image_support.py
+++ b/bob/io/base/test/test_image_support.py
@@ -9,14 +9,14 @@
 """
 
 import os
-import numpy
-
-from bob.io.base import load, write
-from ..test_utils import datafile, temporary_filename
 
 # import bob.io.image
 import nose
+import numpy
+
+from bob.io.base import load, write
 
+from ..test_utils import datafile, temporary_filename
 
 # These are some global parameters for the test.
 PNG_INDEXED_COLOR = datafile("img_indexed_color.png", __name__)
@@ -148,7 +148,9 @@ def test_image_load():
 
     # Testing exception
     nose.tools.assert_raises(
-        RuntimeError, lambda x: load(os.path.splitext(x)[0] + ".unknown"), full_file
+        RuntimeError,
+        lambda x: load(os.path.splitext(x)[0] + ".unknown"),
+        full_file,
     )
 
 
diff --git a/bob/io/base/test/test_io.py b/bob/io/base/test/test_io.py
index b4ec9f684e3eda7e58b781e80f5c9e2a682e3b38..22b8fca959749fccdda33926e509d624693e64d6 100644
--- a/bob/io/base/test/test_io.py
+++ b/bob/io/base/test/test_io.py
@@ -1,7 +1,10 @@
+import os
+
 import nose
 import numpy as np
-import os
-from bob.io.base import vstack_features, save, load
+
+from bob.io.base import load, save, vstack_features
+
 from ..test_utils import temporary_filename
 
 
diff --git a/bob/io/base/test_utils.py b/bob/io/base/test_utils.py
index 8e83915644dd7821572fea0eb8f1c4b751f9a432..6d8f3a059f1048ba23c8e481a138718dc2735d8d 100644
--- a/bob/io/base/test_utils.py
+++ b/bob/io/base/test_utils.py
@@ -8,106 +8,113 @@
 """Re-usable decorators and utilities for bob test code
 """
 
-import os
 import functools
+import os
+
 import nose.plugins.skip
 
-def datafile(f, module=None, path='data'):
-  """datafile(f, [module], [data]) -> filename
 
-  Returns the test file on the "data" subdirectory of the current module.
+def datafile(f, module=None, path="data"):
+    """datafile(f, [module], [data]) -> filename
+
+    Returns the test file on the "data" subdirectory of the current module.
+
+    **Parameters:**
+
+    ``f`` : str
+      This is the filename of the file you want to retrieve. Something like ``'movie.avi'``.
 
-  **Parameters:**
+    ``module``: str
+      [optional] This is the python-style package name of the module you want to retrieve
+      the data from. This should be something like ``bob.io.base``, but you
+      normally refer it using the ``__name__`` property of the module you want to
+      find the path relative to.
 
-  ``f`` : str
-    This is the filename of the file you want to retrieve. Something like ``'movie.avi'``.
+    ``path``: str
+      [Default: ``'data'``] The subdirectory where the datafile will be taken from inside the module.
+      It can be set to ``None`` if it should be taken from the module path root (where the ``__init__.py`` file sits).
 
-  ``module``: str
-    [optional] This is the python-style package name of the module you want to retrieve
-    the data from. This should be something like ``bob.io.base``, but you
-    normally refer it using the ``__name__`` property of the module you want to
-    find the path relative to.
+    **Returns:**
 
-  ``path``: str
-    [Default: ``'data'``] The subdirectory where the datafile will be taken from inside the module.
-    It can be set to ``None`` if it should be taken from the module path root (where the ``__init__.py`` file sits).
+    ``filename`` : str
+      The full path of the file
+    """
 
-  **Returns:**
+    resource = __name__ if module is None else module
+    final_path = f if path is None else os.path.join(path, f)
+    import pkg_resources
 
-  ``filename`` : str
-    The full path of the file
-  """
+    return pkg_resources.resource_filename(resource, final_path)
 
-  resource = __name__ if module is None else module
-  final_path = f if path is None else os.path.join(path, f)
-  import pkg_resources
-  return pkg_resources.resource_filename(resource, final_path)
 
+def temporary_filename(prefix="bobtest_", suffix=".hdf5"):
+    """temporary_filename([prefix], [suffix]) -> filename
 
-def temporary_filename(prefix='bobtest_', suffix='.hdf5'):
-  """temporary_filename([prefix], [suffix]) -> filename
+    Generates a temporary filename to be used in tests, using the default ``temp`` directory (on Unix-like systems, usually ``/tmp``).
+    Please note that you are responsible for deleting the file after your test finished.
+    A common way to assure the file to be deleted is:
 
-  Generates a temporary filename to be used in tests, using the default ``temp`` directory (on Unix-like systems, usually ``/tmp``).
-  Please note that you are responsible for deleting the file after your test finished.
-  A common way to assure the file to be deleted is:
+    .. code-block:: py
 
-  .. code-block:: py
+       import bob.io.base.test_utils
+       temp = bob.io.base.test_utils.temporary_filename()
+       try:
+         # use the temp file
+         ...
+       finally:
+         if os.path.exist(temp): os.remove(temp)
 
-     import bob.io.base.test_utils
-     temp = bob.io.base.test_utils.temporary_filename()
-     try:
-       # use the temp file
-       ...
-     finally:
-       if os.path.exist(temp): os.remove(temp)
+    **Parameters:**
 
-  **Parameters:**
+    ``prefix`` : str
+      [Default: ``'bobtest_'``] The file name prefix to be added in front of the random file name
 
-  ``prefix`` : str
-    [Default: ``'bobtest_'``] The file name prefix to be added in front of the random file name
+    ``suffix`` : str
+      [Default: ``'.hdf5'``] The file name extension of the temporary file name
 
-  ``suffix`` : str
-    [Default: ``'.hdf5'``] The file name extension of the temporary file name
+    **Returns:**
 
-  **Returns:**
+    ``filename`` : str
+      The name of a temporary file that you can use in your test.
+      Don't forget to delete!
 
-  ``filename`` : str
-    The name of a temporary file that you can use in your test.
-    Don't forget to delete!
+    """
+    import tempfile
 
-  """
-  import tempfile
-  fd, name = tempfile.mkstemp(suffix, prefix)
-  os.close(fd)
-  os.unlink(name)
-  return name
+    fd, name = tempfile.mkstemp(suffix, prefix)
+    os.close(fd)
+    os.unlink(name)
+    return name
 
 
 def extension_available(extension):
-  '''Decorator to check if a extension is available before enabling a test
+    """Decorator to check if a extension is available before enabling a test
 
-  This decorator is mainly used to decorate a test function, in order to skip tests when the extension is not available.
-  The syntax is:
+    This decorator is mainly used to decorate a test function, in order to skip tests when the extension is not available.
+    The syntax is:
 
-  .. code-block:: py
+    .. code-block:: py
 
-     import bob.io.base.test_utils
+       import bob.io.base.test_utils
 
-     @bob.io.base.test_utils.extension_available('.ext')
-     def my_test():
-       ...
-  '''
+       @bob.io.base.test_utils.extension_available('.ext')
+       def my_test():
+         ...
+    """
 
-  def test_wrapper(test):
+    def test_wrapper(test):
+        @functools.wraps(test)
+        def wrapper(*args, **kwargs):
+            from . import extensions
 
-    @functools.wraps(test)
-    def wrapper(*args, **kwargs):
-      from . import extensions
-      if extension in extensions():
-        return test(*args, **kwargs)
-      else:
-        raise nose.plugins.skip.SkipTest('Extension to handle "%s" files was not available at compile time' % extension)
+            if extension in extensions():
+                return test(*args, **kwargs)
+            else:
+                raise nose.plugins.skip.SkipTest(
+                    'Extension to handle "%s" files was not available at compile time'
+                    % extension
+                )
 
-    return wrapper
+        return wrapper
 
-  return test_wrapper
+    return test_wrapper
diff --git a/bob/io/image.py b/bob/io/image.py
index 832225afd44bdce7f81516575fcdfb5ee881b703..6776e3c4d64f0e9077744def7d27c4a6111d3fa2 100644
--- a/bob/io/image.py
+++ b/bob/io/image.py
@@ -1,4 +1,5 @@
 import numpy as np
+
 from PIL import Image
 
 
diff --git a/buildout.cfg b/buildout.cfg
index 7d772471c6cf238ea79f3053f215f2b13e0e50d1..0fa626f9f74dcd900ca604a1a430cf7d8143415d 100644
--- a/buildout.cfg
+++ b/buildout.cfg
@@ -10,4 +10,4 @@ newest = false
 verbose = true
 
 [scripts]
-recipe = bob.buildout:scripts
\ No newline at end of file
+recipe = bob.buildout:scripts
diff --git a/doc/conf.py b/doc/conf.py
index ea466ee8a3329c275287ed6e90ca6f16c4599670..5d4e765a85492283d2f65eb950e4cbf1eb95f2d7 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -2,32 +2,29 @@
 # vim: set fileencoding=utf-8 :
 
 import os
-import sys
-import glob
-import pkg_resources
 
+import pkg_resources
 
 # -- General configuration -----------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
-needs_sphinx = '1.3'
+needs_sphinx = "1.3"
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
 extensions = [
-    'sphinx.ext.todo',
-    'sphinx.ext.coverage',
-    'sphinx.ext.ifconfig',
-    'sphinx.ext.autodoc',
-    'sphinx.ext.autosummary',
-    'sphinx.ext.doctest',
-    'sphinx.ext.graphviz',
-    'sphinx.ext.intersphinx',
-    'sphinx.ext.napoleon',
-    'sphinx.ext.viewcode',
-    'sphinx.ext.mathjax',
-    #'matplotlib.sphinxext.plot_directive'
-    ]
+    "sphinx.ext.todo",
+    "sphinx.ext.coverage",
+    "sphinx.ext.ifconfig",
+    "sphinx.ext.autodoc",
+    "sphinx.ext.autosummary",
+    "sphinx.ext.doctest",
+    "sphinx.ext.graphviz",
+    "sphinx.ext.intersphinx",
+    "sphinx.ext.napoleon",
+    "sphinx.ext.viewcode",
+    "sphinx.ext.mathjax",
+]
 
 # Be picky about warnings
 nitpicky = False
@@ -36,13 +33,13 @@ nitpicky = False
 nitpick_ignore = []
 
 # Allows the user to override warnings from a separate file
-if os.path.exists('nitpick-exceptions.txt'):
-    for line in open('nitpick-exceptions.txt'):
+if os.path.exists("nitpick-exceptions.txt"):
+    for line in open("nitpick-exceptions.txt"):
         if line.strip() == "" or line.startswith("#"):
             continue
         dtype, target = line.split(None, 1)
         target = target.strip()
-        try: # python 2.x
+        try:  # python 2.x
             target = unicode(target)
         except NameError:
             pass
@@ -58,25 +55,27 @@ autosummary_generate = True
 numfig = True
 
 # If we are on OSX, the 'dvipng' path maybe different
-dvipng_osx = '/opt/local/libexec/texlive/binaries/dvipng'
-if os.path.exists(dvipng_osx): pngmath_dvipng = dvipng_osx
+dvipng_osx = "/opt/local/libexec/texlive/binaries/dvipng"
+if os.path.exists(dvipng_osx):
+    pngmath_dvipng = dvipng_osx
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
 
 # The suffix of source filenames.
-source_suffix = '.rst'
+source_suffix = ".rst"
 
 # The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
 
 # The master toctree document.
-master_doc = 'index'
+master_doc = "index"
 
 # General information about the project.
-project = u'bob.io.base'
+project = "bob.io.base"
 import time
-copyright = u'%s, Idiap Research Institute' % time.strftime('%Y')
+
+copyright = "%s, Idiap Research Institute" % time.strftime("%Y")
 
 # Grab the setup entry
 distribution = pkg_resources.require(project)[0]
@@ -92,42 +91,42 @@ release = distribution.version
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
-#language = None
+# language = None
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
-#today = ''
+# today = ''
 # Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
-exclude_patterns = ['links.rst']
+exclude_patterns = ["links.rst"]
 
 # The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
+# default_role = None
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
 
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
-#show_authors = False
+# show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
 
 # A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
 
 # Some variables which are useful for generated material
-project_variable = project.replace('.', '_')
-short_description = u'Basic IO for Bob'
-owner = [u'Idiap Research Institute']
+project_variable = project.replace(".", "_")
+short_description = "Basic IO for Bob"
+owner = ["Idiap Research Institute"]
 
 
 # -- Options for HTML output ---------------------------------------------------
@@ -135,80 +134,81 @@ owner = [u'Idiap Research Institute']
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
 import sphinx_rtd_theme
-html_theme = 'sphinx_rtd_theme'
+
+html_theme = "sphinx_rtd_theme"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
-#html_theme_options = {}
+# html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
 html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".
-#html_title = None
+# html_title = None
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = project_variable
+# html_short_title = project_variable
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
-html_logo = 'img/logo.png'
+html_logo = "img/logo.png"
 
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
-html_favicon = 'img/favicon.ico'
+html_favicon = "img/favicon.ico"
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-#html_static_path = ['_static']
+# html_static_path = ['_static']
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
 
 # Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
 
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
 
 # If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
 
 # If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
 
 # If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
 
 # If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
 
 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
 
 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
 
 # This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
 
 # Output file base name for HTML help builder.
-htmlhelp_basename = project_variable + u'_doc'
+htmlhelp_basename = project_variable + "_doc"
 
 
 # -- Post configuration --------------------------------------------------------
@@ -218,26 +218,27 @@ rst_epilog = """
 .. |project| replace:: Bob
 .. |version| replace:: %s
 .. |current-year| date:: %%Y
-""" % (version,)
+""" % (
+    version,
+)
 
 # Default processing flags for sphinx
-autoclass_content = 'class'
-autodoc_member_order = 'bysource'
+autoclass_content = "class"
+autodoc_member_order = "bysource"
 autodoc_default_options = {
-  "members": True,
-  "undoc-members": True,
-  "show-inheritance": True,
+    "members": True,
+    "undoc-members": True,
+    "show-inheritance": True,
 }
 
 # For inter-documentation mapping:
 from bob.extension.utils import link_documentation, load_requirements
+
 sphinx_requirements = "extra-intersphinx.txt"
 if os.path.exists(sphinx_requirements):
-  intersphinx_mapping = link_documentation(
-      additional_packages=['python','numpy'] + \
-      load_requirements(sphinx_requirements)
-      )
+    intersphinx_mapping = link_documentation(
+        additional_packages=["python", "numpy"]
+        + load_requirements(sphinx_requirements)
+    )
 else:
-  intersphinx_mapping = link_documentation()
-
-
+    intersphinx_mapping = link_documentation()
diff --git a/doc/extra-intersphinx.txt b/doc/extra-intersphinx.txt
index 9c61c73639974afe3eafae36df0f4c8f36a72206..9a635b910d93ff2d9b1c24b7bc7ce15600b65d31 100644
--- a/doc/extra-intersphinx.txt
+++ b/doc/extra-intersphinx.txt
@@ -1 +1 @@
-scipy
\ No newline at end of file
+scipy
diff --git a/doc/index.rst b/doc/index.rst
index 74ade76611a9b7954f0f7e8fdfd6a44817f9f477..3e0e720755ca5b5348233bb5c84dd632de20b758 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -13,7 +13,7 @@ Below is the old documentation.
 
 This module contains a basic interface to read and write files of various
 types.  It provides generic functions :py:func:`bob.io.base.save` and
-:py:func:`bob.io.base.load` to write and read various types of data. 
+:py:func:`bob.io.base.load` to write and read various types of data.
 To enable further types of IO, please import one of the following
 packages (the list might not be exhaustive):
 
@@ -25,7 +25,7 @@ Documentation
 .. toctree::
    :maxdepth: 2
 
-   py_api   
+   py_api
 
 TODO
 ----
diff --git a/doc/py_api.rst b/doc/py_api.rst
index bec56c499f404a46c25d909bdad0e191c5d57959..2373795e8d98312e3ea84f9af57c83d680d101cd 100644
--- a/doc/py_api.rst
+++ b/doc/py_api.rst
@@ -16,6 +16,3 @@ Functions
    bob.io.base.save
    bob.io.base.create_directories_safe
    bob.io.base.vstack_features
-
-
-
diff --git a/pyproject.toml b/pyproject.toml
index bb5e83cb40b5871b321beb790ae14606b010e4ca..b738dc847ff9705c5769673db7415f2eb9a75f4d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,3 +1,12 @@
 [build-system]
-requires = ["setuptools", "wheel", "bob.extension"]
-build-backend = "setuptools.build_meta"
+    requires = ["setuptools", "wheel", "bob.extension"]
+    build-backend = "setuptools.build_meta"
+
+[tool.isort]
+    profile = "black"
+    line_length = 80
+    order_by_type = true
+    lines_between_types = 1
+
+[tool.black]
+    line-length = 80
diff --git a/requirements.txt b/requirements.txt
index 33a37b06361e73d0284f0d31668e365593104f9d..32506f993451e5f98c4342c48604b630ef0e59d3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 bob.extension
 h5py
 imageio
-numpy
\ No newline at end of file
+numpy
diff --git a/setup.py b/setup.py
index 99c943d9a5f182e1ec0025264f3fb9ceb9fa9444..b435850c14ce55c1e5a9e83ebfe17b8dc7825bc2 100644
--- a/setup.py
+++ b/setup.py
@@ -3,11 +3,11 @@
 # Andre Anjos <andre.anjos@idiap.ch>
 # Mon 16 Apr 08:18:08 2012 CEST
 #
-from setuptools import setup, dist
+from setuptools import dist, setup
 
 dist.Distribution(dict(setup_requires=["bob.extension"]))
 
-from bob.extension.utils import load_requirements, find_packages
+from bob.extension.utils import find_packages, load_requirements
 
 install_requires = load_requirements()