diff --git a/.editorconfig b/.editorconfig
index adc8c9f724a1bf935bb232bf0c9a3b768569ff15..2a4c182c786edccff66d8f14ee57948b3d4bc382 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -15,4 +15,4 @@ trim_trailing_whitespace = true
 
 [*.py]
 indent_size = 4
-line_length = 88
+line_length = 100
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 6042a3e5ab227c1c5dbc7afe3cc7f3afc973dc71..6b02abe4685d98620a23fdacddf993f71209cb60 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -10,13 +10,19 @@ repos:
     exclude: ^tests/fixtures
   - id: check-yaml
   repo: https://github.com/pre-commit/pre-commit-hooks
-  rev: v4.0.1
+  rev: v4.5.0
+- hooks:
+  - id: ruff
+    args: [--fix, --exit-non-zero-on-fix]
+  - id: ruff-format
+  repo: https://github.com/astral-sh/ruff-pre-commit
+  rev: v0.4.9
 - hooks:
   - id: commitizen
     stages:
     - commit-msg
   repo: https://github.com/commitizen-tools/commitizen
-  rev: v2.20.0
+  rev: v3.27.0
 - hooks:
   - id: darglint
   repo: https://github.com/terrencepreilly/darglint
diff --git a/.pylintrc b/.pylintrc
deleted file mode 100644
index ac4e47a831d131ddd7344148f17497921f4471e3..0000000000000000000000000000000000000000
--- a/.pylintrc
+++ /dev/null
@@ -1,564 +0,0 @@
-# SPDX-FileCopyrightText: 2022 Georg-August-Universität Göttingen
-#
-# SPDX-License-Identifier: CC0-1.0
-
-[MASTER]
-
-# A comma-separated list of package or module names from where C extensions may
-# be loaded. Extensions are loading into the active Python interpreter and may
-# run arbitrary code.
-extension-pkg-allow-list=
-
-# A comma-separated list of package or module names from where C extensions may
-# be loaded. Extensions are loading into the active Python interpreter and may
-# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
-# for backward compatibility.)
-extension-pkg-whitelist=
-
-# Return non-zero exit code if any of these messages/categories are detected,
-# even if score is above --fail-under value. Syntax same as enable. Messages
-# specified are enabled, while categories only check already-enabled messages.
-fail-on=
-
-# Specify a score threshold to be exceeded before program exits with error.
-fail-under=10.0
-
-# Files or directories to be skipped. They should be base names, not paths.
-ignore=CVS
-
-# Add files or directories matching the regex patterns to the ignore-list. The
-# regex matches against paths.
-ignore-paths=
-
-# Files or directories matching the regex patterns are skipped. The regex
-# matches against base names, not paths.
-ignore-patterns=
-
-# Python code to execute, usually for sys.path manipulation such as
-# pygtk.require().
-#init-hook=
-
-# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
-# number of processors available to use.
-jobs=1
-
-# Control the amount of potential inferred values when inferring a single
-# object. This can help the performance when dealing with large functions or
-# complex, nested conditions.
-limit-inference-results=100
-
-# List of plugins (as comma separated values of python module names) to load,
-# usually to register additional checkers.
-load-plugins=
-
-# Pickle collected data for later comparisons.
-persistent=yes
-
-# Min Python version to use for version dependend checks. Will default to the
-# version used to run pylint.
-py-version=3.8
-
-# When enabled, pylint would attempt to guess common misconfiguration and emit
-# user-friendly hints instead of false-positive error messages.
-suggestion-mode=yes
-
-# Allow loading of arbitrary C extensions. Extensions are imported into the
-# active Python interpreter and may run arbitrary code.
-unsafe-load-any-extension=no
-
-
-[MESSAGES CONTROL]
-
-# Only show warnings with the listed confidence levels. Leave empty to show
-# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
-confidence=
-
-# Disable the message, report, category or checker with the given id(s). You
-# can either give multiple identifiers separated by comma (,) or put this
-# option multiple times (only on the command line, not in the configuration
-# file where it should appear only once). You can also use "--disable=all" to
-# disable everything first and then reenable specific checks. For example, if
-# you want to run only the similarities checker, you can use "--disable=all
-# --enable=similarities". If you want to run only the classes checker, but have
-# no Warning level messages displayed, use "--disable=all --enable=classes
-# --disable=W".
-disable=raw-checker-failed,
-        bad-inline-option,
-        locally-disabled,
-        file-ignored,
-        suppressed-message,
-        useless-suppression,
-        deprecated-pragma,
-        use-symbolic-message-instead
-
-# Enable the message, report, category or checker with the given id(s). You can
-# either give multiple identifier separated by comma (,) or put this option
-# multiple time (only on the command line, not in the configuration file where
-# it should appear only once). See also the "--disable" option for examples.
-enable=c-extension-no-member
-
-
-[REPORTS]
-
-# Python expression which should return a score less than or equal to 10. You
-# have access to the variables 'error', 'warning', 'refactor', and 'convention'
-# which contain the number of messages in each category, as well as 'statement'
-# which is the total number of statements analyzed. This score is used by the
-# global evaluation report (RP0004).
-evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
-
-# Template used to display messages. This is a python new-style format string
-# used to format the message information. See doc for all details.
-#msg-template=
-
-# Set the output format. Available formats are text, parseable, colorized, json
-# and msvs (visual studio). You can also give a reporter class, e.g.
-# mypackage.mymodule.MyReporterClass.
-output-format=text
-
-# Tells whether to display a full report or only the messages.
-reports=no
-
-# Activate the evaluation score.
-score=yes
-
-
-[REFACTORING]
-
-# Maximum number of nested blocks for function / method body
-max-nested-blocks=5
-
-# Complete name of functions that never returns. When checking for
-# inconsistent-return-statements if a never returning function is called then
-# it will be considered as an explicit return statement and no message will be
-# printed.
-never-returning-functions=sys.exit,argparse.parse_error
-
-
-[VARIABLES]
-
-# List of additional names supposed to be defined in builtins. Remember that
-# you should avoid defining new builtins when possible.
-additional-builtins=
-
-# Tells whether unused global variables should be treated as a violation.
-allow-global-unused-variables=yes
-
-# List of names allowed to shadow builtins
-allowed-redefined-builtins=
-
-# List of strings which can identify a callback function by name. A callback
-# name must start or end with one of those strings.
-callbacks=cb_,
-          _cb
-
-# A regular expression matching the name of dummy variables (i.e. expected to
-# not be used).
-dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
-
-# Argument names that match this expression will be ignored. Default to name
-# with leading underscore.
-ignored-argument-names=_.*|^ignored_|^unused_
-
-# Tells whether we should check for unused import in __init__ files.
-init-import=no
-
-# List of qualified module names which can have objects that can redefine
-# builtins.
-redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
-
-
-[BASIC]
-
-# Naming style matching correct argument names.
-argument-naming-style=snake_case
-
-# Regular expression matching correct argument names. Overrides argument-
-# naming-style.
-#argument-rgx=
-
-# Naming style matching correct attribute names.
-attr-naming-style=snake_case
-
-# Regular expression matching correct attribute names. Overrides attr-naming-
-# style.
-#attr-rgx=
-
-# Bad variable names which should always be refused, separated by a comma.
-bad-names=foo,
-          bar,
-          baz,
-          toto,
-          tutu,
-          tata
-
-# Bad variable names regexes, separated by a comma. If names match any regex,
-# they will always be refused
-bad-names-rgxs=
-
-# Naming style matching correct class attribute names.
-class-attribute-naming-style=any
-
-# Regular expression matching correct class attribute names. Overrides class-
-# attribute-naming-style.
-#class-attribute-rgx=
-
-# Naming style matching correct class constant names.
-class-const-naming-style=UPPER_CASE
-
-# Regular expression matching correct class constant names. Overrides class-
-# const-naming-style.
-#class-const-rgx=
-
-# Naming style matching correct class names.
-class-naming-style=PascalCase
-
-# Regular expression matching correct class names. Overrides class-naming-
-# style.
-#class-rgx=
-
-# Naming style matching correct constant names.
-const-naming-style=UPPER_CASE
-
-# Regular expression matching correct constant names. Overrides const-naming-
-# style.
-#const-rgx=
-
-# Minimum line length for functions/classes that require docstrings, shorter
-# ones are exempt.
-docstring-min-length=-1
-
-# Naming style matching correct function names.
-function-naming-style=snake_case
-
-# Regular expression matching correct function names. Overrides function-
-# naming-style.
-#function-rgx=
-
-# Good variable names which should always be accepted, separated by a comma.
-good-names=i,
-           j,
-           k,
-           ex,
-           Run,
-           _
-
-# Good variable names regexes, separated by a comma. If names match any regex,
-# they will always be accepted
-good-names-rgxs=
-
-# Include a hint for the correct naming format with invalid-name.
-include-naming-hint=no
-
-# Naming style matching correct inline iteration names.
-inlinevar-naming-style=any
-
-# Regular expression matching correct inline iteration names. Overrides
-# inlinevar-naming-style.
-#inlinevar-rgx=
-
-# Naming style matching correct method names.
-method-naming-style=snake_case
-
-# Regular expression matching correct method names. Overrides method-naming-
-# style.
-#method-rgx=
-
-# Naming style matching correct module names.
-module-naming-style=snake_case
-
-# Regular expression matching correct module names. Overrides module-naming-
-# style.
-#module-rgx=
-
-# Colon-delimited sets of names that determine each other's naming style when
-# the name regexes allow several styles.
-name-group=
-
-# Regular expression which should only match function or class names that do
-# not require a docstring.
-no-docstring-rgx=^_
-
-# List of decorators that produce properties, such as abc.abstractproperty. Add
-# to this list to register other decorators that produce valid properties.
-# These decorators are taken in consideration only for invalid-name.
-property-classes=abc.abstractproperty
-
-# Naming style matching correct variable names.
-variable-naming-style=snake_case
-
-# Regular expression matching correct variable names. Overrides variable-
-# naming-style.
-#variable-rgx=
-
-
-[FORMAT]
-
-# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
-expected-line-ending-format=
-
-# Regexp for a line that is allowed to be longer than the limit.
-ignore-long-lines=^\s*(# )?<?https?://\S+>?$
-
-# Number of spaces of indent required inside a hanging or continued line.
-indent-after-paren=4
-
-# String used as indentation unit. This is usually "    " (4 spaces) or "\t" (1
-# tab).
-indent-string='    '
-
-# Maximum number of characters on a single line.
-max-line-length=100
-
-# Maximum number of lines in a module.
-max-module-lines=1000
-
-# Allow the body of a class to be on the same line as the declaration if body
-# contains single statement.
-single-line-class-stmt=no
-
-# Allow the body of an if to be on the same line as the test if there is no
-# else.
-single-line-if-stmt=no
-
-
-[SIMILARITIES]
-
-# Comments are removed from the similarity computation
-ignore-comments=yes
-
-# Docstrings are removed from the similarity computation
-ignore-docstrings=yes
-
-# Imports are removed from the similarity computation
-ignore-imports=no
-
-# Signatures are removed from the similarity computation
-ignore-signatures=no
-
-# Minimum lines number of a similarity.
-min-similarity-lines=4
-
-
-[SPELLING]
-
-# Limits count of emitted suggestions for spelling mistakes.
-max-spelling-suggestions=4
-
-# Spelling dictionary name. Available dictionaries: none. To make it work,
-# install the 'python-enchant' package.
-spelling-dict=
-
-# List of comma separated words that should be considered directives if they
-# appear and the beginning of a comment and should not be checked.
-spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
-
-# List of comma separated words that should not be checked.
-spelling-ignore-words=
-
-# A path to a file that contains the private dictionary; one word per line.
-spelling-private-dict-file=
-
-# Tells whether to store unknown words to the private dictionary (see the
-# --spelling-private-dict-file option) instead of raising a message.
-spelling-store-unknown-words=no
-
-
-[STRING]
-
-# This flag controls whether inconsistent-quotes generates a warning when the
-# character used as a quote delimiter is used inconsistently within a module.
-check-quote-consistency=yes
-
-# This flag controls whether the implicit-str-concat should generate a warning
-# on implicit string concatenation in sequences defined over several lines.
-check-str-concat-over-line-jumps=yes
-
-
-[LOGGING]
-
-# The type of string formatting that logging methods do. `old` means using %
-# formatting, `new` is for `{}` formatting.
-logging-format-style=old
-
-# Logging modules to check that the string format arguments are in logging
-# function parameter format.
-logging-modules=logging
-
-
-[TYPECHECK]
-
-# List of decorators that produce context managers, such as
-# contextlib.contextmanager. Add to this list to register other decorators that
-# produce valid context managers.
-contextmanager-decorators=contextlib.contextmanager
-
-# List of members which are set dynamically and missed by pylint inference
-# system, and so shouldn't trigger E1101 when accessed. Python regular
-# expressions are accepted.
-generated-members=
-
-# Tells whether missing members accessed in mixin class should be ignored. A
-# mixin class is detected if its name ends with "mixin" (case insensitive).
-ignore-mixin-members=yes
-
-# Tells whether to warn about missing members when the owner of the attribute
-# is inferred to be None.
-ignore-none=yes
-
-# This flag controls whether pylint should warn about no-member and similar
-# checks whenever an opaque object is returned when inferring. The inference
-# can return multiple potential results while evaluating a Python object, but
-# some branches might not be evaluated, which results in partial inference. In
-# that case, it might be useful to still emit no-member and other checks for
-# the rest of the inferred objects.
-ignore-on-opaque-inference=yes
-
-# List of class names for which member attributes should not be checked (useful
-# for classes with dynamically set attributes). This supports the use of
-# qualified names.
-ignored-classes=optparse.Values,thread._local,_thread._local
-
-# List of module names for which member attributes should not be checked
-# (useful for modules/projects where namespaces are manipulated during runtime
-# and thus existing member attributes cannot be deduced by static analysis). It
-# supports qualified module names, as well as Unix pattern matching.
-ignored-modules=
-
-# Show a hint with possible names when a member name was not found. The aspect
-# of finding the hint is based on edit distance.
-missing-member-hint=yes
-
-# The minimum edit distance a name should have in order to be considered a
-# similar match for a missing member name.
-missing-member-hint-distance=1
-
-# The total number of similar names that should be taken in consideration when
-# showing a hint for a missing member.
-missing-member-max-choices=1
-
-# List of decorators that change the signature of a decorated function.
-signature-mutators=
-
-
-[MISCELLANEOUS]
-
-# List of note tags to take in consideration, separated by a comma.
-notes=FIXME,
-      XXX,
-      TODO
-
-# Regular expression of note tags to take in consideration.
-#notes-rgx=
-
-
-[IMPORTS]
-
-# List of modules that can be imported at any level, not just the top level
-# one.
-allow-any-import-level=
-
-# Allow wildcard imports from modules that define __all__.
-allow-wildcard-with-all=no
-
-# Analyse import fallback blocks. This can be used to support both Python 2 and
-# 3 compatible code, which means that the block might have code that exists
-# only in one or another interpreter, leading to false positives when analysed.
-analyse-fallback-blocks=no
-
-# Deprecated modules which should not be used, separated by a comma.
-deprecated-modules=
-
-# Output a graph (.gv or any supported image format) of external dependencies
-# to the given file (report RP0402 must not be disabled).
-ext-import-graph=
-
-# Output a graph (.gv or any supported image format) of all (i.e. internal and
-# external) dependencies to the given file (report RP0402 must not be
-# disabled).
-import-graph=
-
-# Output a graph (.gv or any supported image format) of internal dependencies
-# to the given file (report RP0402 must not be disabled).
-int-import-graph=
-
-# Force import order to recognize a module as part of the standard
-# compatibility libraries.
-known-standard-library=
-
-# Force import order to recognize a module as part of a third party library.
-known-third-party=enchant
-
-# Couples of modules and preferred modules, separated by a comma.
-preferred-modules=
-
-
-[CLASSES]
-
-# Warn about protected attribute access inside special methods
-check-protected-access-in-special-methods=no
-
-# List of method names used to declare (i.e. assign) instance attributes.
-defining-attr-methods=__init__,
-                      __new__,
-                      setUp,
-                      __post_init__
-
-# List of member names, which should be excluded from the protected access
-# warning.
-exclude-protected=_asdict,
-                  _fields,
-                  _replace,
-                  _source,
-                  _make
-
-# List of valid names for the first argument in a class method.
-valid-classmethod-first-arg=cls
-
-# List of valid names for the first argument in a metaclass class method.
-valid-metaclass-classmethod-first-arg=cls
-
-
-[DESIGN]
-
-# List of qualified class names to ignore when counting class parents (see
-# R0901)
-ignored-parents=
-
-# Maximum number of arguments for function / method.
-max-args=5
-
-# Maximum number of attributes for a class (see R0902).
-max-attributes=7
-
-# Maximum number of boolean expressions in an if statement (see R0916).
-max-bool-expr=5
-
-# Maximum number of branch for function / method body.
-max-branches=12
-
-# Maximum number of locals for function / method body.
-max-locals=15
-
-# Maximum number of parents for a class (see R0901).
-max-parents=7
-
-# Maximum number of public methods for a class (see R0904).
-max-public-methods=20
-
-# Maximum number of return / yield for function / method body.
-max-returns=6
-
-# Maximum number of statements in function / method body.
-max-statements=50
-
-# Minimum number of public methods for a class (see R0903).
-min-public-methods=2
-
-
-[EXCEPTIONS]
-
-# Exceptions that will emit a warning when being caught. Defaults to
-# "BaseException, Exception".
-overgeneral-exceptions=BaseException,
-                       Exception
diff --git a/.vscode/settings.json b/.vscode/settings.json
index aeb1ebe51c04180e9616078d90c2fe6565cc7b9f..5f262aaf62aea3344fba287a7330bf195e6d3b11 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,8 +1,5 @@
 {
-    "python.formatting.provider": "autopep8",
     "python.defaultInterpreterPath": "venv/bin/python",
-    "pylint.enabled": true,
-    "pylint.path": ["venv/bin/pylint"],
     "ruff.enable": true,
     "python.testing.pytestArgs": [
         "tests",
diff --git a/conftest.py b/conftest.py
index 2b2e879267f9ee28ebb3bc13b7ee7ada376acda5..7f300cf63b2c8bfdeca4e9252caba549a154d85d 100644
--- a/conftest.py
+++ b/conftest.py
@@ -2,12 +2,14 @@
 #
 # SPDX-License-Identifier: CC0-1.0
 
-import pytest
 
 # pytest finds added options only in root conftest.py if not configured otherwise
 def pytest_addoption(parser):
-    parser.addoption("--integration", action="store_true", default=False, help="run integration tests")
+    parser.addoption(
+        '--integration', action='store_true', default=False, help='run integration tests'
+    )
+
 
 # list marker if requested with 'pytest --markers'
 def pytest_configure(config):
-    config.addinivalue_line("markers", "integration: mark a test as integration test")
+    config.addinivalue_line('markers', 'integration: mark a test as integration test')
diff --git a/docs/conf.py b/docs/conf.py
index 9d95d77a8529303eb0b27017b60d0861205e2e27..f4c9e2dfe9236d3ef5f2bc244528e87c548305bd 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -16,6 +16,7 @@
 #
 import os
 import sys
+
 import tgclients
 
 sys.path.insert(0, os.path.abspath('..'))
@@ -29,10 +30,10 @@ author = 'Stefan Hynek, Ubbo Veentjer'
 
 
 # The short X.Y version.
-#version = '.'.join(tgclients.__version__.split('.')[0:2])
+# version = '.'.join(tgclients.__version__.split('.')[0:2])
 
 # The full version, including alpha/beta/rc tags
-#release = tgclients.__version__
+# release = tgclients.__version__
 
 version = tgclients.__version__
 
@@ -43,13 +44,13 @@ version = tgclients.__version__
 # ones.
 extensions = [
     'sphinx.ext.autodoc',
-    'sphinx.ext.napoleon',    # https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html
-    'sphinx.ext.intersphinx', # https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html
+    'sphinx.ext.napoleon',  # https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html
+    'sphinx.ext.intersphinx',  # https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html
     # decide wether to use myst_parser & nbshinx or myst_nb
     #    'myst_parser',       # https://myst-parser.readthedocs.io/en/latest/
     #    'nbsphinx'
     'myst_nb',
-    'IPython.sphinxext.ipython_console_highlighting', # https://github.com/spatialaudio/nbsphinx/issues/687
+    'IPython.sphinxext.ipython_console_highlighting',  # https://github.com/spatialaudio/nbsphinx/issues/687
 ]
 
 # Add any paths that contain templates here, relative to this directory.
@@ -79,7 +80,7 @@ html_theme = 'sphinx_rtd_theme'
 # 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_favicon = '_static/favicon.ico'
+# html_favicon = '_static/favicon.ico'
 html_favicon = 'favicon.ico'
 
 # Example configuration for intersphinx: refer to the Python standard library.
@@ -91,7 +92,7 @@ intersphinx_mapping = {
 
 # autodoc options - https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html
 autodoc_typehints_format = 'short'
-#autodoc_class_signature = 'separated'
+# autodoc_class_signature = 'separated'
 
 myst_enable_extensions = [
     'tasklist',
diff --git a/pyproject.toml b/pyproject.toml
index 4d241e7b49166901801993ea95b0811ca737abe6..7fcf336eaa05549924bacaffff335231d82067ec 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,3 +5,26 @@
 [build-system]
 requires = ["setuptools"]
 build-backend = "setuptools.build_meta"
+
+[tool.ruff]
+exclude = [
+    "src/tgclients/databinding",
+]
+
+line-length = 100
+
+[tool.ruff.lint]
+# Enable `pydocstyle` rules, in addition to the defaults.
+select = ["E4", "E7", "E9", "F", "D"]
+# ignore "Missing docstring in `__init__`" because its opposed to DOC301 from pydocstyle
+ignore = ["D107"]
+
+[tool.ruff.lint.per-file-ignores]
+# Ignore `D` rules everywhere except for the `src/` directory.
+"!src/**.py" = ["D"]
+
+[tool.ruff.lint.pydocstyle]
+convention = "google"
+
+[tool.ruff.format]
+quote-style = "single"
diff --git a/setup.cfg b/setup.cfg
index 7019db69a8d387bbde82999eb8eda2addcf6a414..c231f065fe26c442e85bd1755416965c23abeb6a 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -45,21 +45,18 @@ install_requires =
 
 [options.extras_require]
 dev =
-    autopep8
-    bandit
     commitizen
     coverage
     darglint
     ipykernel
     ipywidgets
-    mypy
     pip-tools
     pre-commit
-    pylint
     pytest
     requests-mock
     reuse
     rstcheck
+    ruff
     Sphinx
     testbook
     tqdm
diff --git a/src/tgclients/__init__.py b/src/tgclients/__init__.py
index ab2abfda68133f52776e258870b4ce660ee4b79c..d4a07df1b65c6e8ec88a0b7cb3b15c7eb8cc5e25 100644
--- a/src/tgclients/__init__.py
+++ b/src/tgclients/__init__.py
@@ -2,6 +2,8 @@
 #
 # SPDX-License-Identifier: CC0-1.0
 
+"""tgclients provide access to TextGrid services."""
+
 __version__ = '0.16.0'
 
 from tgclients.aggregator import (
@@ -16,20 +18,21 @@ from tgclients.config import (
 )
 from tgclients.crud import (
     TextgridCrud,
-    TextgridCrudRequest,
     TextgridCrudException,
+    TextgridCrudRequest,
 )
 from tgclients.metadata import (
     TextgridMetadata,
 )
 from tgclients.search import (
     TextgridSearch,
-    TextgridSearchRequest,
     TextgridSearchException,
+    TextgridSearchRequest,
 )
 from tgclients.utils import (
     Utils,
 )
+
 __all__ = [
     'Aggregator',
     'TextgridAuth',
@@ -38,6 +41,7 @@ __all__ = [
     'TextgridCrud',
     'TextgridCrudRequest',
     'TextgridCrudException',
+    'TextgridMetadata',
     'TextgridSearch',
     'TextgridSearchRequest',
     'TextgridSearchException',
diff --git a/src/tgclients/aggregator.py b/src/tgclients/aggregator.py
index 73d111510098c21e68e5a5d3f0ee8f5d2781795b..0166953024f2d9b7f534dca16fbd71a4368343a5 100644
--- a/src/tgclients/aggregator.py
+++ b/src/tgclients/aggregator.py
@@ -2,7 +2,8 @@
 #
 # SPDX-License-Identifier: LGPL-3.0-or-later
 
-"""API for the TextGrid aggregator service"""
+"""API for the TextGrid aggregator service."""
+
 import logging
 from typing import List, Optional, Union, overload
 
@@ -16,7 +17,9 @@ logger = logging.getLogger(__name__)
 
 class Aggregator:
     """Provide access to the Textgrid Aggregator Service.
-    API docs: https://textgridlab.org/doc/services/submodules/aggregator/docs/api.html"""
+
+    API docs: https://textgridlab.org/doc/services/submodules/aggregator/docs/api.html
+    """
 
     def __init__(self, config: TextgridConfig = TextgridConfig()) -> None:
         self._url = config.aggregator
@@ -24,15 +27,14 @@ class Aggregator:
         self._requests = requests.Session()
 
     @overload
-    def zip(self, textgrid_uris: str, sid: Optional[str] = None) -> Response:
-        ...
+    def zip(self, textgrid_uris: str, sid: Optional[str] = None) -> Response: ...
 
     @overload
-    def zip(self, textgrid_uris: List[str], sid: Optional[str] = None) -> Response:
-        ...
+    def zip(self, textgrid_uris: List[str], sid: Optional[str] = None) -> Response: ...
 
     def zip(self, textgrid_uris: Union[str, List[str]], sid: Optional[str] = None) -> Response:
         """Download aggregated TextGrid objects as ZIP file.
+
         https://textgridlab.org/doc/services/submodules/aggregator/docs/zip.html
 
         Args:
@@ -45,17 +47,16 @@ class Aggregator:
         if isinstance(textgrid_uris, list):
             textgrid_uris = ','.join(textgrid_uris)
         url = self._url + '/zip/'
-        response = self._requests.get(url + textgrid_uris, params={ 'sid': sid },
-                                timeout=self._config.http_timeout)
+        response = self._requests.get(
+            url + textgrid_uris, params={'sid': sid}, timeout=self._config.http_timeout
+        )
         return response
 
     @overload
-    def text(self, textgrid_uris: str, sid: Optional[str] = None) -> Response:
-        ...
+    def text(self, textgrid_uris: str, sid: Optional[str] = None) -> Response: ...
 
     @overload
-    def text(self, textgrid_uris: List[str], sid: Optional[str] = None) -> Response:
-        ...
+    def text(self, textgrid_uris: List[str], sid: Optional[str] = None) -> Response: ...
 
     # python 3.10 allows writinh Union as |
     # https://www.blog.pythonlibrary.org/2021/09/11/python-3-10-simplifies-unions-in-type-annotations/
@@ -72,20 +73,20 @@ class Aggregator:
         if isinstance(textgrid_uris, list):
             textgrid_uris = ','.join(textgrid_uris)
         url = self._url + '/text/'
-        response = self._requests.get(url + textgrid_uris, params={ 'sid': sid },
-                                timeout=self._config.http_timeout)
+        response = self._requests.get(
+            url + textgrid_uris, params={'sid': sid}, timeout=self._config.http_timeout
+        )
         return response
 
     @overload
-    def teicorpus(self, textgrid_uris: str, sid: Optional[str] = None) -> Response:
-        ...
+    def teicorpus(self, textgrid_uris: str, sid: Optional[str] = None) -> Response: ...
 
     @overload
-    def teicorpus(self, textgrid_uris: List[str], sid: Optional[str] = None) -> Response:
-        ...
+    def teicorpus(self, textgrid_uris: List[str], sid: Optional[str] = None) -> Response: ...
 
-    def teicorpus(self, textgrid_uris: Union[str, List[str]], sid: Optional[str] = None
-        ) -> Response:
+    def teicorpus(
+        self, textgrid_uris: Union[str, List[str]], sid: Optional[str] = None
+    ) -> Response:
         """Download aggregated TextGrid objects as TEI corpus.
 
         Args:
@@ -98,40 +99,44 @@ class Aggregator:
         if isinstance(textgrid_uris, list):
             textgrid_uris = ','.join(textgrid_uris)
         url = self._url + '/teicorpus/'
-        response = self._requests.get(url + textgrid_uris, params={ 'sid': sid },
-                                timeout=self._config.http_timeout)
+        response = self._requests.get(
+            url + textgrid_uris, params={'sid': sid}, timeout=self._config.http_timeout
+        )
         return response
 
     @overload
-    def render(self,
+    def render(
+        self,
         textgrid_uris: str,
         sid: Optional[str] = None,
         stylesheet_uri: Optional[str] = None,
         mediatype: Optional[str] = None,
         link_pattern: Optional[str] = None,
-        sandbox: Optional[bool] = None
-        ) -> Response:
-        ...
+        sandbox: Optional[bool] = None,
+    ) -> Response: ...
 
     @overload
-    def render(self, textgrid_uris: List[str],
+    def render(
+        self,
+        textgrid_uris: List[str],
         sid: Optional[str] = None,
         stylesheet_uri: Optional[str] = None,
         mediatype: Optional[str] = None,
         link_pattern: Optional[str] = None,
-        sandbox: Optional[bool] = None
-        ) -> Response:
-        ...
+        sandbox: Optional[bool] = None,
+    ) -> Response: ...
 
-    def render(self,
+    def render(
+        self,
         textgrid_uris: Union[str, List[str]],
         sid: Optional[str] = None,
         stylesheet_uri: Optional[str] = None,
         mediatype: Optional[str] = None,
         link_pattern: Optional[str] = None,
-        sandbox: Optional[bool] = None
-        ) -> Response:
+        sandbox: Optional[bool] = None,
+    ) -> Response:
         """Apply an XSLT stylesheet to one or more TextGrid URIs.
+
         Will render (X)HTML by default with XSLT stylesheets from tei-c.org
         see https://textgridlab.org/doc/services/submodules/aggregator/docs/html.html
 
@@ -152,13 +157,15 @@ class Aggregator:
         if isinstance(textgrid_uris, list):
             textgrid_uris = ','.join(textgrid_uris)
         url = self._url + '/html/'
-        response = self._requests.get(url + textgrid_uris,
-                                params={
-                                    'sid': sid,
-                                    'stylesheet': stylesheet_uri,
-                                    'mediatype': mediatype,
-                                    'linkPattern': link_pattern,
-                                    'sandbox': sandbox
-                                },
-                                timeout=self._config.http_timeout)
+        response = self._requests.get(
+            url + textgrid_uris,
+            params={
+                'sid': sid,
+                'stylesheet': stylesheet_uri,
+                'mediatype': mediatype,
+                'linkPattern': link_pattern,
+                'sandbox': sandbox,
+            },
+            timeout=self._config.http_timeout,
+        )
         return response
diff --git a/src/tgclients/auth.py b/src/tgclients/auth.py
index 4d89678212272cdd10f4828772fb8952752a7276..64ac167691f53df41aada32851f1e2072618872b 100644
--- a/src/tgclients/auth.py
+++ b/src/tgclients/auth.py
@@ -3,8 +3,9 @@
 # SPDX-License-Identifier: LGPL-3.0-or-later
 
 """Provide access to the TextGrid Authorization Service."""
+
 import logging
-from typing import Optional, List
+from typing import List, Optional
 
 from zeep import Client
 from zeep.exceptions import Fault, TransportError
@@ -30,7 +31,9 @@ class TextgridAuth:
         self._extra_crud_client = self._connect_extra_crud()
 
     def _connect(self) -> Client:
-        """Internal helper that provides a SOAP client that is configured for
+        """Create standard SOAP client.
+
+        Internal helper that provides a SOAP client that is configured for
         the use with the Textgrid Auth service.
 
         Returns:
@@ -42,7 +45,9 @@ class TextgridAuth:
         return client
 
     def _connect_extra_crud(self) -> Client:
-        """Internal helper that provides a SOAP client that is configured for
+        """Create tgextra SOAP client.
+
+        Internal helper that provides a SOAP client that is configured for
         the use with the Textgrid Auth service (the extra crud service).
 
         Returns:
@@ -73,7 +78,9 @@ class TextgridAuth:
             raise TextgridAuthException(message) from error
 
     def list_all_projects(self) -> List[str]:
-        """Returns all projects stored in this RBAC instance with ID, name,
+        """List all projects.
+
+        Returns all projects stored in this RBAC instance with ID, name,
         and description. See also getProjectDescription(). SID
         is not needed as this information can be reviewed publicly.
 
@@ -94,9 +101,10 @@ class TextgridAuth:
         """
         return self._client.service.getProjectDescription('', '', project_id)
 
-    def create_project(self, sid: str, name: str, description: str,
-                       default_owner_roles: Optional[bool] = True) -> str:
-        """Create a new project
+    def create_project(
+        self, sid: str, name: str, description: str, default_owner_roles: Optional[bool] = True
+    ) -> str:
+        """Create a new project.
 
         Args:
             sid (str): TextGrid Session ID
@@ -112,8 +120,9 @@ class TextgridAuth:
             str: the project ID of the created project
         """
         try:
-            project_id = self._client.service.createProject(auth=sid, name=name,
-                                                            description=description)
+            project_id = self._client.service.createProject(
+                auth=sid, name=name, description=description
+            )
         except Fault as fault:
             message = 'Error creating project. Is your sessionID valid?'
             logger.warning(message)
@@ -127,7 +136,7 @@ class TextgridAuth:
         return project_id
 
     def get_eppn_for_sid(self, sid: str) -> str:
-        """Get the EPPN belonging to a sessionID
+        """Get the EPPN belonging to a sessionID.
 
         Args:
             sid (str): TextGrid Session ID
@@ -147,7 +156,7 @@ class TextgridAuth:
         return eppn
 
     def delete_project(self, sid: str, project_id: str) -> bool:
-        """Delete a project
+        """Delete a project.
 
         Args:
             sid (str): TextGrid Session ID
@@ -160,8 +169,7 @@ class TextgridAuth:
             bool: true in case of success
         """
         try:
-            status = self._client.service.deleteProject(
-                auth=sid, project=project_id)
+            status = self._client.service.deleteProject(auth=sid, project=project_id)
         except Fault as fault:
             message = 'Error deleting project. Is your sessionID valid?'
             logger.warning(message)
@@ -169,7 +177,7 @@ class TextgridAuth:
         return status
 
     def add_admin_to_project(self, sid: str, project_id: str, eppn: str) -> bool:
-        """Give an user the admin role in a project
+        """Give an user the admin role in a project.
 
         Args:
             sid (str): TextGrid Session ID
@@ -188,7 +196,7 @@ class TextgridAuth:
             raise exception
 
     def add_editor_to_project(self, sid: str, project_id: str, eppn: str) -> bool:
-        """Give an user the editor role in a project
+        """Give an user the editor role in a project.
 
         Args:
             sid (str): TextGrid Session ID
@@ -207,7 +215,7 @@ class TextgridAuth:
             raise exception
 
     def add_manager_to_project(self, sid: str, project_id: str, eppn: str) -> bool:
-        """Give an user the manager role in a project
+        """Give an user the manager role in a project.
 
         Args:
             sid (str): TextGrid Session ID
@@ -226,7 +234,7 @@ class TextgridAuth:
             raise exception
 
     def add_observer_to_project(self, sid: str, project_id: str, eppn: str) -> bool:
-        """Give an user the observer role in a project
+        """Give an user the observer role in a project.
 
         Args:
             sid (str): TextGrid Session ID
@@ -247,8 +255,7 @@ class TextgridAuth:
     def _add_role_to_project(self, sid: str, project_id: str, eppn: str, role: str) -> bool:
         rolename = role + ',' + project_id + ',Projekt-Teilnehmer'
         try:
-            status = self._client.service.addMember(
-                auth=sid, username=eppn, role=rolename)
+            status = self._client.service.addMember(auth=sid, username=eppn, role=rolename)
         except Fault as fault:
             message = 'Error adding role to project. Is your sessionID valid?'
             logger.warning(message)
@@ -257,4 +264,4 @@ class TextgridAuth:
 
 
 class TextgridAuthException(Exception):
-    """Exception communicating with tgauth"""
+    """Exception communicating with tgauth!"""
diff --git a/src/tgclients/config.py b/src/tgclients/config.py
index b1bb99c418e520e4340d26adfc5284210c761f76..855aac46fe8729397ce68f2bc79c97744e483406 100644
--- a/src/tgclients/config.py
+++ b/src/tgclients/config.py
@@ -3,6 +3,7 @@
 # SPDX-License-Identifier: CC0-1.0
 
 """Variable config options with defaults to be used with the TextGrid clients library."""
+
 import logging
 from typing import Optional
 
@@ -17,6 +18,7 @@ TEST_SERVER: str = 'https://test.textgridlab.org'
 
 class TextgridConfig:
     """Provide standard configuration / URLs for TextGrid services.
+
     Default is to connect to the TextGrid
     production server (https://textgridlab.org).
     Pass the constants tgclients.config.DEV_SERVER or
@@ -37,8 +39,7 @@ class TextgridConfig:
             host (Optional[str]):TextGrid server. Defaults to 'https://textgridlab.org'.
         """
         if not isinstance(host, str) or host == '':
-            logger.info(
-                'host param was None or emtpy, default to: %s', PROD_SERVER)
+            logger.info('host param was None or emtpy, default to: %s', PROD_SERVER)
             host = PROD_SERVER
         if host.endswith('/'):
             logger.info('trailing slash in hostname detected and removed')
@@ -48,7 +49,7 @@ class TextgridConfig:
 
     @property
     def host(self) -> str:
-        """the host URL
+        """The host URL.
 
         Returns:
             str: the configured host URL
@@ -57,16 +58,16 @@ class TextgridConfig:
 
     @property
     def auth_wsdl(self) -> str:
-        """the tgauth wsdl location
+        """The tgauth WSDL location.
 
         Returns:
-            str: the tgauth wsdl location
+            str: the tgauth WSDL location
         """
         return self._host + '/1.0/tgauth/wsdl/tgextra.wsdl'
 
     @property
     def auth_address(self) -> str:
-        """the tgauth service location
+        """The tgauth service location.
 
         Returns:
             str: the tgauth service location
@@ -75,16 +76,16 @@ class TextgridConfig:
 
     @property
     def extra_crud_wsdl(self) -> str:
-        """the tgextra wsdl location
+        """The tgextra WSDL location.
 
         Returns:
-            str: the tgextra wsdl location
+            str: the tgextra WSDL location
         """
         return self._host + '/1.0/tgauth/wsdl/tgextra-crud.wsdl'
 
     @property
     def extra_crud_address(self) -> str:
-        """the tgextra service location
+        """The tgextra service location.
 
         Returns:
             str: the tgextra service location
@@ -93,7 +94,7 @@ class TextgridConfig:
 
     @property
     def search(self) -> str:
-        """the nonpublic tgsearch service location
+        """The nonpublic tgsearch service location.
 
         Returns:
             str: the nonpublic tgsearch service location
@@ -102,7 +103,7 @@ class TextgridConfig:
 
     @property
     def search_public(self) -> str:
-        """the public tgsearch service location
+        """The public tgsearch service location.
 
         Returns:
             str: the public tgsearch service location
@@ -111,7 +112,7 @@ class TextgridConfig:
 
     @property
     def crud(self) -> str:
-        """the nonpublic tgcrud REST service location
+        """The nonpublic tgcrud REST service location.
 
         Returns:
             str: the nonpublic tgcrud REST service location
@@ -120,7 +121,7 @@ class TextgridConfig:
 
     @property
     def crud_public(self) -> str:
-        """the public tgcrud REST service location
+        """The public tgcrud REST service location.
 
         Returns:
             str: the public tgcrud REST service location
@@ -129,7 +130,7 @@ class TextgridConfig:
 
     @property
     def aggregator(self) -> str:
-        """the aggregator service location
+        """The aggregator service location.
 
         Returns:
             str: the aggregator service location
@@ -138,7 +139,7 @@ class TextgridConfig:
 
     @property
     def http_timeout(self) -> float:
-        """HTTP timeout to be used when accessing TextGrid services
+        """HTTP timeout to be used when accessing TextGrid services.
 
         Returns:
             float: http timeout in seconds
@@ -147,7 +148,7 @@ class TextgridConfig:
 
     @http_timeout.setter
     def http_timeout(self, value: float) -> None:
-        """Set HTTP timeout to be used when accessing TextGrid services
+        """Set HTTP timeout to be used when accessing TextGrid services.
 
         Args:
             value (float): the timeout
diff --git a/src/tgclients/crud.py b/src/tgclients/crud.py
index e168c812a93f7b9f156adc002d58c523b451068f..92200f2f2a2117a3b0f48400d6d06e7b3d803d6a 100644
--- a/src/tgclients/crud.py
+++ b/src/tgclients/crud.py
@@ -3,13 +3,13 @@
 # SPDX-License-Identifier: LGPL-3.0-or-later
 
 """TextGrid CRUD API."""
+
 import logging
 from io import BytesIO
 from typing import Optional
 
-from bs4 import BeautifulSoup
-
 import requests
+from bs4 import BeautifulSoup
 from requests.models import Response
 from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor
 from xsdata.formats.dataclass.context import XmlContext
@@ -26,11 +26,14 @@ RESPONSE_ENCODING = 'utf-8'
 class TextgridCrudRequest:
     """Provide low level access to the TextGrid CRUD Service."""
 
-    def __init__(self, config: TextgridConfig = TextgridConfig(),
-                 for_publication: bool = False) -> None:
+    def __init__(
+        self, config: TextgridConfig = TextgridConfig(), for_publication: bool = False
+    ) -> None:
         if for_publication:
-            logger.warning('for_publication set. this tgcrud client is able to publish data to '
-                           + 'the public repository, please make sure you know what you are doing.')
+            logger.warning(
+                'for_publication set. this tgcrud client is able to publish data to '
+                + 'the public repository, please make sure you know what you are doing.'
+            )
             self._url = config.crud_public
         else:
             self._url = config.crud
@@ -45,7 +48,7 @@ class TextgridCrudRequest:
         self._serializer = XmlSerializer()
 
     def read_data(self, textgrid_uri: str, sid: Optional[str] = None) -> Response:
-        """Read Data
+        """Read Data.
 
         Args:
             textgrid_uri (str): Textgrid URI
@@ -58,14 +61,16 @@ class TextgridCrudRequest:
             Response: HTTP response from service
         """
         # defer downloading the response body until accessing Response.content
-        response = self._requests.get(self._url + '/' + textgrid_uri + '/data',
-                                params={'sessionId':  sid},
-                                stream=True,
-                                timeout=self._config.http_timeout)
+        response = self._requests.get(
+            self._url + '/' + textgrid_uri + '/data',
+            params={'sessionId': sid},
+            stream=True,
+            timeout=self._config.http_timeout,
+        )
         return self._handle_response(response)
 
     def read_metadata(self, textgrid_uri: str, sid: Optional[str] = None) -> Response:
-        """Read Metadata
+        """Read Metadata.
 
         Args:
             textgrid_uri (str): Textgrid URI
@@ -77,14 +82,16 @@ class TextgridCrudRequest:
         Returns:
             Response: HTTP response from service
         """
-        response = self._requests.get(self._url + '/' + textgrid_uri + '/metadata',
-                                params={'sessionId':  sid},
-                                stream=True,
-                                timeout=self._config.http_timeout)
+        response = self._requests.get(
+            self._url + '/' + textgrid_uri + '/metadata',
+            params={'sessionId': sid},
+            stream=True,
+            timeout=self._config.http_timeout,
+        )
         return self._handle_response(response)
 
     def create_resource(self, sid: str, project_id: str, data, metadata) -> Response:
-        """Create a TextGrid object
+        """Create a TextGrid object.
 
         Args:
             sid (str): Session ID
@@ -99,16 +106,19 @@ class TextgridCrudRequest:
             Response: HTTP response from service with metadata from newly created object
         """
         encoder = self._prepare_multipart(metadata, data)
-        params = {'sessionId': sid, 'projectId': project_id,
-                  'createRevision': 'false'}
+        params = {'sessionId': sid, 'projectId': project_id, 'createRevision': 'false'}
         response = self._requests.post(
-            self._url + '/' + 'create', params=params, data=encoder,
+            self._url + '/' + 'create',
+            params=params,
+            data=encoder,
             headers={'Content-Type': encoder.content_type},
-            timeout=self._config.http_timeout)
+            timeout=self._config.http_timeout,
+        )
         return self._handle_response(response)
 
-    def create_revision(self, sid: str, project_id: str, textgrid_uri: str,
-                        data, metadata: str) -> Response:
+    def create_revision(
+        self, sid: str, project_id: str, textgrid_uri: str, data, metadata: str
+    ) -> Response:
         """Create a TextGrid object revision.
 
         Args:
@@ -125,17 +135,25 @@ class TextgridCrudRequest:
             Response: HTTP response from service with metadata from newly created object revision
         """
         encoder = self._prepare_multipart(metadata, data)
-        params = {'sessionId': sid, 'uri': textgrid_uri,
-                  'createRevision': 'true', 'projectId': project_id}
+        params = {
+            'sessionId': sid,
+            'uri': textgrid_uri,
+            'createRevision': 'true',
+            'projectId': project_id,
+        }
         response = self._requests.post(
-            self._url + '/' + 'create', params=params, data=encoder,
+            self._url + '/' + 'create',
+            params=params,
+            data=encoder,
             headers={'Content-Type': encoder.content_type},
-            timeout=self._config.http_timeout)
+            timeout=self._config.http_timeout,
+        )
         return self._handle_response(response)
 
-    def update_resource(self, sid: str, textgrid_uri: str, data, metadata,
-                        create_revision: bool = False) -> Response:
-        """Update a TextGrid object
+    def update_resource(
+        self, sid: str, textgrid_uri: str, data, metadata, create_revision: bool = False
+    ) -> Response:
+        """Update a TextGrid object.
 
         Args:
             sid (str): Session ID
@@ -158,13 +176,16 @@ class TextgridCrudRequest:
         encoder = self._prepare_multipart(metadata, data)
         params = {'sessionId': sid}
         response = self._requests.post(
-            self._url + '/' + textgrid_uri + '/update', params=params, data=encoder,
+            self._url + '/' + textgrid_uri + '/update',
+            params=params,
+            data=encoder,
             headers={'Content-Type': encoder.content_type},
-            timeout=self._config.http_timeout)
+            timeout=self._config.http_timeout,
+        )
         return self._handle_response(response)
 
     def update_metadata(self, sid: str, textgrid_uri: str, metadata) -> Response:
-        """Update metadata for TextGrid object
+        """Update metadata for TextGrid object.
 
         Args:
             sid (str): Session ID
@@ -177,13 +198,16 @@ class TextgridCrudRequest:
         encoder = self._prepare_multipart(metadata)
         params = {'sessionId': sid}
         response = self._requests.post(
-            self._url + '/' + textgrid_uri + '/updateMetadata', params=params, data=encoder,
+            self._url + '/' + textgrid_uri + '/updateMetadata',
+            params=params,
+            data=encoder,
             headers={'Content-Type': encoder.content_type},
-            timeout=self._config.http_timeout)
+            timeout=self._config.http_timeout,
+        )
         return self._handle_response(response)
 
     def delete_resource(self, sid: str, textgrid_uri: str) -> Response:
-        """Delete a TextGrid object
+        """Delete a TextGrid object.
 
         Args:
             sid (str): Session ID
@@ -197,13 +221,15 @@ class TextgridCrudRequest:
         """
         params = {'sessionId': sid}
         response = self._requests.get(
-            self._url + '/' + textgrid_uri + '/delete', params=params,
-            timeout=self._config.http_timeout)
+            self._url + '/' + textgrid_uri + '/delete',
+            params=params,
+            timeout=self._config.http_timeout,
+        )
         return self._handle_response(response)
 
     @staticmethod
     def _handle_response(response: Response) -> Response:
-        """Error handling for responses from crud
+        """Error handling for responses from crud.
 
         Args:
             response (Response): a response from tgcrud
@@ -217,16 +243,17 @@ class TextgridCrudRequest:
         response.encoding = RESPONSE_ENCODING
         if not response.ok:
             error = TextgridCrudRequest._error_msg_from_html(response.text)
-            message = '[Error] HTTP Code: ' + \
-                str(response.status_code) + ' - ' + error
+            message = '[Error] HTTP Code: ' + str(response.status_code) + ' - ' + error
             logger.warning(message)
             raise TextgridCrudException(message)
         return response
 
     @staticmethod
     def _error_msg_from_html(html: str):
-        """ Extract error message from html, as the text string for
-        the error is in <meta name="description" content="error message">
+        """Extract error message from html.
+
+        The text string for the error is in
+        <meta name="description" content="error message">.
 
         Args:
             html: an error response body from tgcrud
@@ -237,7 +264,7 @@ class TextgridCrudRequest:
         #
         soup = BeautifulSoup(html, 'html.parser')
         metatag = soup.select_one('meta[name="description"]')
-        if metatag != None:
+        if metatag is not None:
             msg = metatag['content']
         else:
             msg = html[0:255]
@@ -246,6 +273,7 @@ class TextgridCrudRequest:
     @staticmethod
     def _prepare_multipart(metadata, data=None):
         """Create a streaming multipart object.
+
         Monitor the upload progress if log level is DEBUG.
 
         See also: https://toolbelt.readthedocs.io/en/latest/uploading-data.html
@@ -257,12 +285,9 @@ class TextgridCrudRequest:
         Returns:
             [MultipartEncoder]: Multipart containing data and metadata
         """
-        fields = {
-            'tgObjectMetadata': ('tgObjectMetadata', metadata, 'text/xml')
-        }
+        fields = {'tgObjectMetadata': ('tgObjectMetadata', metadata, 'text/xml')}
         if data:
-            fields['tgObjectData'] = (
-                'tgObjectData', data, 'application/octet-stream')
+            fields['tgObjectData'] = ('tgObjectData', data, 'application/octet-stream')
 
         encoder = MultipartEncoder(fields=fields)
         if logger.isEnabledFor(logging.DEBUG):
@@ -273,29 +298,31 @@ class TextgridCrudRequest:
     @staticmethod
     def _debug_monitor_callback(monitor: MultipartEncoderMonitor):
         """Callback for _prepare_multipart.
+
         Helper to log upload progress for streaming multipart when log level is DEBUG.
 
         Args:
             monitor (MultipartEncoderMonitor): the monitor
         """
-        logger.debug('[debug multipart upload] bytes read: %s ',
-                     monitor.bytes_read)
+        logger.debug('[debug multipart upload] bytes read: %s ', monitor.bytes_read)
 
 
 class TextgridCrudException(Exception):
-    """Exception communicating with tgcrud"""
+    """Exception communicating with tgcrud!"""
 
 
 class TextgridCrud(TextgridCrudRequest):
-    """Provide access to the Textgrid CRUD Service using a XML data binding """
+    """Provide access to the Textgrid CRUD Service using a XML data binding."""
 
-    def __init__(self, config: TextgridConfig = TextgridConfig(),
-                 for_publication: bool = False) -> None:
+    def __init__(
+        self, config: TextgridConfig = TextgridConfig(), for_publication: bool = False
+    ) -> None:
         super().__init__(config, for_publication)
 
-    def create_resource(self, sid: str, project_id: str,
-                        data, metadata: MetadataContainerType) -> MetadataContainerType:
-        """Create a TextGrid object
+    def create_resource(
+        self, sid: str, project_id: str, data, metadata: MetadataContainerType
+    ) -> MetadataContainerType:
+        """Create a TextGrid object.
 
         Args:
             sid (str): Session ID
@@ -313,8 +340,9 @@ class TextgridCrud(TextgridCrudRequest):
         response = super().create_resource(sid, project_id, data, metadata_string)
         return self._parser.parse(BytesIO(response.content), MetadataContainerType)
 
-    def create_revision(self, sid: str, project_id: str, textgrid_uri: str,
-                        data, metadata: MetadataContainerType) -> MetadataContainerType:
+    def create_revision(
+        self, sid: str, project_id: str, textgrid_uri: str, data, metadata: MetadataContainerType
+    ) -> MetadataContainerType:
         """Create a TextGrid object revision.
 
         Args:
@@ -335,7 +363,7 @@ class TextgridCrud(TextgridCrudRequest):
         return self._parser.parse(BytesIO(response.content), MetadataContainerType)
 
     def read_metadata(self, textgrid_uri: str, sid: Optional[str] = None) -> MetadataContainerType:
-        """Read Metadata
+        """Read Metadata.
 
         Args:
             textgrid_uri (str): Textgrid URI
@@ -350,9 +378,10 @@ class TextgridCrud(TextgridCrudRequest):
         response = super().read_metadata(textgrid_uri, sid)
         return self._parser.parse(BytesIO(response.content), MetadataContainerType)
 
-    def update_metadata(self, sid: str, textgrid_uri: str,
-                        metadata: MetadataContainerType) -> MetadataContainerType:
-        """Update metadata for TextGrid object
+    def update_metadata(
+        self, sid: str, textgrid_uri: str, metadata: MetadataContainerType
+    ) -> MetadataContainerType:
+        """Update metadata for TextGrid object.
 
         Args:
             sid (str): Session ID
@@ -366,10 +395,15 @@ class TextgridCrud(TextgridCrudRequest):
         response = super().update_metadata(sid, textgrid_uri, metadata_string)
         return self._parser.parse(BytesIO(response.content), MetadataContainerType)
 
-    def update_resource(self, sid: str, textgrid_uri: str,
-                        data, metadata: MetadataContainerType,
-                        create_revision: bool = False) -> MetadataContainerType:
-        """Update a TextGrid object
+    def update_resource(
+        self,
+        sid: str,
+        textgrid_uri: str,
+        data,
+        metadata: MetadataContainerType,
+        create_revision: bool = False,
+    ) -> MetadataContainerType:
+        """Update a TextGrid object.
 
         Args:
             sid (str): Session ID
diff --git a/src/tgclients/metadata.py b/src/tgclients/metadata.py
index aaa50d503f37f05ae38dd4f5511c0eb5147171d5..7f7da54abc3c8bd2b6a7375fac6ac9aa9a055de5 100644
--- a/src/tgclients/metadata.py
+++ b/src/tgclients/metadata.py
@@ -2,10 +2,11 @@
 #
 # SPDX-License-Identifier: LGPL-3.0-or-later
 
-"""Helper functions to work with TextGrid metadata XML"""
+"""Helper functions to work with TextGrid metadata XML."""
+
+import logging
 import os
 import re
-import logging
 from pathlib import Path
 from typing import Optional
 
@@ -13,8 +14,8 @@ from jinja2 import Environment, FileSystemLoader
 from xsdata.formats.dataclass.context import XmlContext
 from xsdata.formats.dataclass.parsers import XmlParser
 
-from tgclients.databinding.tgsearch import Response, ResultType
 from tgclients.databinding.textgrid_metadata_2010 import MetadataContainerType
+from tgclients.databinding.tgsearch import Response, ResultType
 
 try:
     import icu
@@ -23,11 +24,11 @@ except ImportError:
 
 logger = logging.getLogger(__name__)
 
-__location__ = os.path.realpath(
-    os.path.join(os.getcwd(), os.path.dirname(__file__)))
+__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
+
 
 class TextgridMetadata:
-    """Helper functions to work with TextGrid metadata XML"""
+    """Helper functions to work with TextGrid metadata XML."""
 
     def __init__(self):
         context = XmlContext()
@@ -38,7 +39,7 @@ class TextgridMetadata:
 
     @staticmethod
     def create(title: str, mimetype: str) -> str:
-        """Create XML metadata for an TextGrid Object
+        """Create XML metadata for an TextGrid Object.
 
         Args:
             title (str): title of the object
@@ -48,14 +49,13 @@ class TextgridMetadata:
             str: XML metadata as string
         """
         path = Path(__file__).parent / 'templates'
-        env = Environment(
-            loader=FileSystemLoader(Path(path)), autoescape=True)
+        env = Environment(loader=FileSystemLoader(Path(path)), autoescape=True)
         template = env.get_template('metadata.xml.jinja2')
         metadata = template.render(title=title, format=mimetype)
         return metadata
 
     def build(self, title: str, mimetype: str) -> MetadataContainerType:
-        """Build metadata for an TextGrid Object
+        """Build metadata for an TextGrid Object.
 
         Args:
             title (str): title of the object
@@ -68,11 +68,20 @@ class TextgridMetadata:
         return self._parser.from_string(metadata, MetadataContainerType)
 
     def searchresponse2object(self, xml: str) -> Response:
+        """Build databinding for XML string returned from tgsearch.
+
+        Args:
+            xml (str): xml string as returned from tgsearch
+
+        Returns:
+            Response: tgsearch Response
+        """
         return self._parser.from_string(xml, Response)
 
     def filename_from_metadata(self, metadata: ResultType) -> str:
-        """Generate a filename for a textgrid search metadata result
-        containg title, textfgrid-uri and extension
+        """Generate a filename for a textgrid search metadata result.
+
+        This is made of title, textgrid-URI and extension.
 
         Args:
             metadata (ResultType): tgsearch metadata result
@@ -90,8 +99,7 @@ class TextgridMetadata:
         return self.filename(title, uri, mimetype)
 
     def filename(self, title: str, tguri: str, mimetype: str) -> str:
-        """Generate a filename for the triple of
-        title, textfgrid-uri and extension
+        """Generate a filename for the triple of title, textfgrid-uri and extension.
 
         Args:
             title (str): the title
@@ -113,8 +121,7 @@ class TextgridMetadata:
         # converted to python from info.textgrid.utils.export.filenames.FileExtensionMap
         # of link-rewriter (https://gitlab.gwdg.de/dariah-de/textgridrep/link-rewriter)
         extension_map = {}
-        map_line_pattern = re.compile(
-            '^[ \t]*([^# \t]+)[ \t]*([^#]+)[ \t]*(#.*)?$')
+        map_line_pattern = re.compile('^[ \t]*([^# \t]+)[ \t]*([^#]+)[ \t]*(#.*)?$')
         space_pattern = re.compile('[ \t]+')
 
         with open(os.path.join(__location__, 'mime.types'), encoding='utf8') as mimefile:
@@ -132,6 +139,7 @@ class TextgridMetadata:
 
     def extension_for_format(self, mimetype: str) -> Optional[str]:
         """Find a matching extension for a textgrid mime type.
+
         The first matching extension for a mime type is returned, so
         extensions defined first in mime.types will be used.
 
@@ -149,12 +157,22 @@ class TextgridMetadata:
 
     @staticmethod
     def remove_tg_prefix(tguri: str) -> str:
+        """Remove the 'textgrid:' prefix from an textgrid URI.
+
+        Args:
+            tguri (str): the textgrid URI
+
+        Returns:
+            str: uri without the prefix
+        """
         return tguri[9:]
 
     @staticmethod
     def id_from_filename(filename: str) -> str:
-        """extract the id from a filename which is named according to link rewriters
-        textgrid metadata to filename mapping
+        """Extract the id from a filename.
+
+        This is named according to link rewriters
+        textgrid metadata to filename mapping.
 
         Args:
             filename (str): the filename
@@ -166,7 +184,7 @@ class TextgridMetadata:
         next_to_last_dot = filename.rfind('.', 0, last_dot)
         # a textgrid uri has a revision number in the end.
         # if the chars after the last dot are not numeric, we have a filename extension
-        if not filename[last_dot+1:].isnumeric():
+        if not filename[last_dot + 1 :].isnumeric():
             # extension is there? we need the '.' before the dot separating the uri
             # from the revision component
             next_to_last_dot = filename.rfind('.', 0, next_to_last_dot)
@@ -174,10 +192,10 @@ class TextgridMetadata:
             # there is no extension to cut of, we want the end of the string
             last_dot = None
 
-        return filename[next_to_last_dot+1:last_dot]
+        return filename[next_to_last_dot + 1 : last_dot]
 
     def transliterate(self, title: str) -> str:
-        """replace all chars which may be problematic in filenames from title string
+        """Replace all chars which may be problematic in filenames from title string.
 
         Args:
             title (str): a title from textgrid metadata
@@ -198,4 +216,5 @@ class TextgridMetadata:
         with open(os.path.join(__location__, 'tgfilenames.rules'), encoding='utf8') as rulesfile:
             rules = rulesfile.read()
             return icu.Transliterator.createFromRules(
-                'TgFilenames', rules, icu.UTransDirection.FORWARD)
+                'TgFilenames', rules, icu.UTransDirection.FORWARD
+            )
diff --git a/src/tgclients/search.py b/src/tgclients/search.py
index 2ed302eef9838193678e7e436d15b73de810a5c6..40ea32771ba64060d455b3f7f9ca627f9eb9e4c0 100644
--- a/src/tgclients/search.py
+++ b/src/tgclients/search.py
@@ -3,9 +3,10 @@
 # SPDX-License-Identifier: LGPL-3.0-or-later
 
 """TextGrid Search API."""
+
 import logging
 from io import BytesIO
-from typing import Optional, List
+from typing import List, Optional
 
 import requests
 from requests.models import Response
@@ -13,7 +14,8 @@ from xsdata.formats.dataclass.context import XmlContext
 from xsdata.formats.dataclass.parsers import XmlParser
 
 from tgclients.config import TextgridConfig
-from tgclients.databinding.tgsearch import Response as SearchResponse, TextgridUris
+from tgclients.databinding.tgsearch import Response as SearchResponse
+from tgclients.databinding.tgsearch import TextgridUris
 
 logger = logging.getLogger(__name__)
 
@@ -31,8 +33,7 @@ class TextgridSearchRequest:
         self._requests = requests.Session()
 
     def info(self, textgrid_uri: str, sid: Optional[str] = None) -> Response:
-        """Retrieve metadata for a textgrid object specified by its
-        textgrid-uri
+        """Retrieve metadata for a textgrid object specified by its textgrid-uri.
 
         Args:
             textgrid_uri (str): Textgrid URI
@@ -46,12 +47,14 @@ class TextgridSearchRequest:
         """
         url = self._url + '/info/'
         response = self._requests.get(
-            url + textgrid_uri, params={'sid': sid}, timeout=self._config.http_timeout)
+            url + textgrid_uri, params={'sid': sid}, timeout=self._config.http_timeout
+        )
         return self._handle_response(response)
 
     def list_project_root(self, project_id: str, sid: Optional[str] = None) -> Response:
-        """Get objects belonging to a project, filtered by objects that are in
-        an aggregation in the same project.
+        """Get objects belonging to a project.
+
+        These are filtered by objects that are in an aggregation in the same project.
 
         Args:
             project_id (str): the ID of the project to list
@@ -64,8 +67,10 @@ class TextgridSearchRequest:
             Response: HTTP response from service, containing a list of textgrid metadata entries
         """
         response = self._requests.get(
-            self._url + '/navigation/' + project_id, params={'sid': sid},
-            timeout=self._config.http_timeout)
+            self._url + '/navigation/' + project_id,
+            params={'sid': sid},
+            timeout=self._config.http_timeout,
+        )
         return self._handle_response(response)
 
     def list_aggregation(self, textgrid_uri: str, sid: Optional[str] = None) -> Response:
@@ -81,27 +86,31 @@ class TextgridSearchRequest:
         Returns:
             Response: HTTP response from service, containing a list of textgrid metadata entries
         """
-        response = self._requests.get(self._url + '/navigation/agg/' +
-                                textgrid_uri, params={'sid': sid},
-                                timeout=self._config.http_timeout)
+        response = self._requests.get(
+            self._url + '/navigation/agg/' + textgrid_uri,
+            params={'sid': sid},
+            timeout=self._config.http_timeout,
+        )
         return self._handle_response(response)
 
     # pylint: disable-msg=too-many-arguments,too-many-locals
-    def search(self,
-               query: Optional[str] = '*',
-               sid: Optional[str] = None,
-               target: Optional[str] = None,
-               order: Optional[str] = None,
-               start: Optional[int] = None,
-               limit: Optional[int] = None,
-               kwic_width: Optional[int] = None,
-               word_distance: Optional[int] = None,
-               path: Optional[bool] = None,
-               all_projects: Optional[bool] = None,
-               sandbox: Optional[bool] = None,
-               filters: Optional[List[str]] = None,
-               facet:  Optional[List[str]] = None,
-               facet_limit: Optional[int] = None) -> Response:
+    def search(
+        self,
+        query: Optional[str] = '*',
+        sid: Optional[str] = None,
+        target: Optional[str] = None,
+        order: Optional[str] = None,
+        start: Optional[int] = None,
+        limit: Optional[int] = None,
+        kwic_width: Optional[int] = None,
+        word_distance: Optional[int] = None,
+        path: Optional[bool] = None,
+        all_projects: Optional[bool] = None,
+        sandbox: Optional[bool] = None,
+        filters: Optional[List[str]] = None,
+        facet: Optional[List[str]] = None,
+        facet_limit: Optional[int] = None,
+    ) -> Response:
         """Run fulltext queries or filters on TextGrid metadata and fulltext objects.
 
         Please note: as the defaults of this function are mostly set to None, the defaults from
@@ -141,7 +150,6 @@ class TextgridSearchRequest:
             Response: HTTP response from service - a list of textgrid metadata entries,
                       KWIC hits, paths and facets if requested
         """
-
         params = {
             'q': query,
             'sid': sid,
@@ -156,15 +164,17 @@ class TextgridSearchRequest:
             'sandbox': sandbox,
             'filter': filters,
             'facet': facet,
-            'facetLimit': facet_limit
+            'facetLimit': facet_limit,
         }
-        response = self._requests.get(self._url + '/search', params=params,
-                                timeout=self._config.http_timeout)
+        response = self._requests.get(
+            self._url + '/search', params=params, timeout=self._config.http_timeout
+        )
         return self._handle_response(response)
+
     # pylint: enable-msg=too-many-arguments,too-many-locals
 
     def edition_work_metadata_for(self, textgrid_uri: str, sid: Optional[str] = None) -> Response:
-        """Find parent edition for an object and the edition and work metadata
+        """Find parent edition for an object and the edition and work metadata.
 
         Args:
             textgrid_uri (str): Textgrid URI
@@ -178,13 +188,14 @@ class TextgridSearchRequest:
                       from first matching parent edition
         """
         response = self._requests.get(
-            self._url + '/info/' + textgrid_uri +
-            '/editionWorkMeta', params={'sid': sid},
-            timeout=self._config.http_timeout)
+            self._url + '/info/' + textgrid_uri + '/editionWorkMeta',
+            params={'sid': sid},
+            timeout=self._config.http_timeout,
+        )
         return self._handle_response(response)
 
     def children(self, textgrid_uri: str, sid: Optional[str] = None) -> Response:
-        """List URIs for all children of this aggregation and its child aggregations
+        """List URIs for all children of this aggregation and its child aggregations.
 
         Args:
             textgrid_uri (str): Textgrid URI of an aggregation
@@ -198,13 +209,15 @@ class TextgridSearchRequest:
                       aggregation and its child aggregations
         """
         response = self._requests.get(
-            self._url + '/info/' + textgrid_uri + '/children', params={'sid': sid},
-            timeout=self._config.http_timeout)
+            self._url + '/info/' + textgrid_uri + '/children',
+            params={'sid': sid},
+            timeout=self._config.http_timeout,
+        )
         return self._handle_response(response)
 
     @staticmethod
     def _handle_response(response: Response) -> Response:
-        """Error handling for responses from tgsearch
+        """Error handling for responses from tgsearch.
 
         Args:
             response (Response): a response from tgsearch
@@ -216,15 +229,16 @@ class TextgridSearchRequest:
             Response: the response
         """
         if not response.ok:
-            message = '[Error] HTTP Code: ' + \
-                str(response.status_code) + ' - ' + response.text[0:255]
+            message = (
+                '[Error] HTTP Code: ' + str(response.status_code) + ' - ' + response.text[0:255]
+            )
             logger.warning(message)
             raise TextgridSearchException(message)
         return response
 
 
 class TextgridSearch(TextgridSearchRequest):
-    """Provide access to the TextGrid search service using a XML data binding """
+    """Provide access to the TextGrid search service using a XML data binding."""
 
     def __init__(self, config: TextgridConfig = TextgridConfig(), nonpublic: bool = False) -> None:
         super().__init__(config, nonpublic)
@@ -234,8 +248,7 @@ class TextgridSearch(TextgridSearchRequest):
         self._parser = XmlParser(context=context)
 
     def info(self, textgrid_uri: str, sid: Optional[str] = None) -> SearchResponse:
-        """Retrieve metadata for a textgrid object specified by its
-        textgrid-uri
+        """Retrieve metadata for a textgrid object specified by its textgrid-uri.
 
         Args:
             textgrid_uri (str): Textgrid URI
@@ -251,8 +264,9 @@ class TextgridSearch(TextgridSearchRequest):
         return self._parser.parse(BytesIO(response.content), SearchResponse)
 
     def list_project_root(self, project_id: str, sid: Optional[str] = None) -> SearchResponse:
-        """Get objects belonging to a project, filtered by objects that are in
-        an aggregation in the same project.
+        """Get objects belonging to a project.
+
+        These are filtered by objects that are in an aggregation in the same project.
 
         Args:
             project_id (str): the ID of the project to list
@@ -285,21 +299,23 @@ class TextgridSearch(TextgridSearchRequest):
 
     # pylint: disable-msg=too-many-arguments,too-many-locals
 
-    def search(self,
-               query: Optional[str] = '*',
-               sid: Optional[str] = None,
-               target: Optional[str] = None,
-               order: Optional[str] = None,
-               start: Optional[int] = None,
-               limit: Optional[int] = None,
-               kwic_width: Optional[int] = None,
-               word_distance: Optional[int] = None,
-               path: Optional[bool] = None,
-               all_projects: Optional[bool] = None,
-               sandbox: Optional[bool] = None,
-               filters: Optional[List[str]] = None,
-               facet:  Optional[List[str]] = None,
-               facet_limit: Optional[int] = None) -> SearchResponse:
+    def search(
+        self,
+        query: Optional[str] = '*',
+        sid: Optional[str] = None,
+        target: Optional[str] = None,
+        order: Optional[str] = None,
+        start: Optional[int] = None,
+        limit: Optional[int] = None,
+        kwic_width: Optional[int] = None,
+        word_distance: Optional[int] = None,
+        path: Optional[bool] = None,
+        all_projects: Optional[bool] = None,
+        sandbox: Optional[bool] = None,
+        filters: Optional[List[str]] = None,
+        facet: Optional[List[str]] = None,
+        facet_limit: Optional[int] = None,
+    ) -> SearchResponse:
         """Run fulltext queries or filters on TextGrid metadata and fulltext objects.
 
         Please note: as the defaults of this function are mostly set to None, the defaults from
@@ -339,27 +355,30 @@ class TextgridSearch(TextgridSearchRequest):
             SearchResponse: a list of textgrid metadata entries,
                             KWIC hits, paths and facets if requested
         """
-
-        response = super().search(query=query,
-                                  sid=sid,
-                                  target=target,
-                                  order=order,
-                                  start=start,
-                                  limit=limit,
-                                  kwic_width=kwic_width,
-                                  word_distance=word_distance,
-                                  path=path,
-                                  all_projects=all_projects,
-                                  sandbox=sandbox,
-                                  filters=filters,
-                                  facet=facet,
-                                  facet_limit=facet_limit)
+        response = super().search(
+            query=query,
+            sid=sid,
+            target=target,
+            order=order,
+            start=start,
+            limit=limit,
+            kwic_width=kwic_width,
+            word_distance=word_distance,
+            path=path,
+            all_projects=all_projects,
+            sandbox=sandbox,
+            filters=filters,
+            facet=facet,
+            facet_limit=facet_limit,
+        )
         return self._parser.parse(BytesIO(response.content), SearchResponse)
+
     # pylint: enable-msg=too-many-arguments,too-many-locals
 
-    def edition_work_metadata_for(self, textgrid_uri: str,
-                                  sid: Optional[str] = None) -> SearchResponse:
-        """Find parent edition for an object and the edition and work metadata
+    def edition_work_metadata_for(
+        self, textgrid_uri: str, sid: Optional[str] = None
+    ) -> SearchResponse:
+        """Find parent edition for an object and the edition and work metadata.
 
         Args:
             textgrid_uri (str): Textgrid URI
@@ -376,7 +395,7 @@ class TextgridSearch(TextgridSearchRequest):
         return self._parser.parse(BytesIO(response.content), SearchResponse)
 
     def children(self, textgrid_uri: str, sid: Optional[str] = None) -> TextgridUris:
-        """List URIs for all children of this aggregation and its child aggregations
+        """List URIs for all children of this aggregation and its child aggregations.
 
         Args:
             textgrid_uri (str): Textgrid URI of an aggregation
@@ -393,4 +412,4 @@ class TextgridSearch(TextgridSearchRequest):
 
 
 class TextgridSearchException(Exception):
-    """Exception communicating with tgsearch"""
+    """Exception communicating with tgsearch!"""
diff --git a/src/tgclients/utils.py b/src/tgclients/utils.py
index 904074827c9e3d47bc5596ee14adf2c977dd2e05..c8795141ca3d2472badbce4e3bcf4908371aa90c 100644
--- a/src/tgclients/utils.py
+++ b/src/tgclients/utils.py
@@ -2,22 +2,21 @@
 #
 # SPDX-License-Identifier: LGPL-3.0-or-later
 
-"""Utility functions for working with the TextGrid repository"""
+"""Utility functions for working with the TextGrid repository."""
+
 from pathlib import Path
 from typing import List
 
 import defusedxml.ElementTree as ET
 from jinja2 import Environment, FileSystemLoader
 
-from tgclients.databinding.tgsearch import Response as SearchResponse
-
 
 class Utils:
-    """Utility functions for working with the TextGrid repository"""
+    """Utility functions for working with the TextGrid repository."""
 
     @staticmethod
     def list_to_aggregation(textgrid_uri: str, members: List[str]) -> str:
-        """Create XML for a TextGrid aggregation from list
+        """Create XML for a TextGrid aggregation from list.
 
         Args:
             textgrid_uri (str): textgrid URI of the aggregation to create
@@ -27,15 +26,14 @@ class Utils:
             str: XML for TextGrid Aggregation
         """
         path = Path(__file__).parent / 'templates'
-        env = Environment(
-            loader=FileSystemLoader(Path(path)), autoescape=True)
+        env = Environment(loader=FileSystemLoader(Path(path)), autoescape=True)
         template = env.get_template('aggregation.xml.jinja2')
         aggregation = template.render(id=textgrid_uri, members=members)
         return aggregation
 
     @staticmethod
     def aggregation_to_list(xml: str) -> List[str]:
-        """Extract URIs from TextGrid aggregation into a list
+        """Extract URIs from TextGrid aggregation into a list.
 
         Args:
             xml (str): TextGrid aggregation XML
diff --git a/tests/conftest.py b/tests/conftest.py
index b58980c651c38375c22ea2518ee1e95fa2ff6712..f10d1b492fc93baaf7741b33fcfe8afbfcd5312b 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -17,49 +17,58 @@ def pytest_collection_modifyitems(config, items):
     if config.getoption('--integration'):
         # --integration given in cli: do not skip integration tests
         return
-    skip_integration = pytest.mark.skip(
-        reason='need --integration option to run')
+    skip_integration = pytest.mark.skip(reason='need --integration option to run')
     for item in items:
         if 'integration' in item.keywords:
             item.add_marker(skip_integration)
 
+
 if os.getenv('TEXTGRID_HOST') is not None:
     _tgconfig = TextgridConfig(os.getenv('TEXTGRID_HOST'))
 else:
     _tgconfig = TextgridConfig()
 
+
 @pytest.fixture
 def tgconfig() -> TextgridConfig:
     return _tgconfig
 
+
 @pytest.fixture
 def search_public() -> TextgridSearch:
     return TextgridSearch(_tgconfig)
 
+
 @pytest.fixture
 def searchrequest_public() -> TextgridSearchRequest:
     return TextgridSearchRequest(_tgconfig)
 
+
 @pytest.fixture
 def crud_public():
     return TextgridCrud(_tgconfig, for_publication=True)
 
+
 @pytest.fixture
 def crud():
     return TextgridCrud(_tgconfig)
 
+
 @pytest.fixture
 def crud_request_public():
     return TextgridCrudRequest(_tgconfig, for_publication=True)
 
+
 @pytest.fixture
 def crud_request():
     return TextgridCrudRequest(_tgconfig)
 
+
 @pytest.fixture
 def auth():
     return TextgridAuth(_tgconfig)
 
+
 @pytest.fixture
 def aggregator():
     return Aggregator(_tgconfig)
diff --git a/tests/integration/test_aggregatgor_integration.py b/tests/integration/test_aggregatgor_integration.py
index 4849936077679108fd3b632c84667310dcba1bf0..3bbfa56785cad87d08bc5a34cb2d242cca06a0b3 100644
--- a/tests/integration/test_aggregatgor_integration.py
+++ b/tests/integration/test_aggregatgor_integration.py
@@ -4,40 +4,34 @@
 import pytest
 
 
-
 @pytest.mark.integration
 class TestAggregatorIntegration:
-
     @staticmethod
     def test_zip_single_uri(aggregator):
-        """ get a zip file for a single uri should be succesfull # noqa: DAR101
-        """
+        """get a zip file for a single uri should be succesfull # noqa: DAR101"""
         uri = 'textgrid:kv2q.0'
         res = aggregator.zip(uri)
         assert res.status_code == 200
 
     @staticmethod
     def test_zip_urilist(aggregator):
-        """ get a zip file for a single uri should be succesfull # noqa: DAR101
-        """
+        """get a zip file for a single uri should be succesfull # noqa: DAR101"""
         uris = ['textgrid:kv2v.0', 'textgrid:kv2q.0']
         res = aggregator.zip(uris)
         assert res.status_code == 200
 
     @staticmethod
     def test_single_text(aggregator):
-        """get plain text for single uri # noqa: DAR101
-        """
+        """get plain text for single uri # noqa: DAR101"""
         uri = 'textgrid:kv2q.0'
         res = aggregator.text(uri)
         assert res.status_code == 200
-        assert res.text.startswith('Lewis Carroll\nAlice\'s Abenteuer im Wunderland')
+        assert res.text.startswith("Lewis Carroll\nAlice's Abenteuer im Wunderland")
         assert res.text.endswith('an ihr eigenes Kindesleben und die glücklichen Sommertage.\n')
 
     @staticmethod
     def test_more_text_uri_string(aggregator):
-        """get plain text for two uris (as string) # noqa: DAR101
-        """
+        """get plain text for two uris (as string) # noqa: DAR101"""
         uri = 'textgrid:kv2v.0,textgrid:kv2q.0'
         res = aggregator.text(uri)
         assert res.status_code == 200
@@ -46,18 +40,16 @@ class TestAggregatorIntegration:
 
     @staticmethod
     def test_more_text_uri_string_different_order(aggregator):
-        """get plain text for two uris (as string) in reverse order # noqa: DAR101
-        """
+        """get plain text for two uris (as string) in reverse order # noqa: DAR101"""
         uri = 'textgrid:kv2q.0,textgrid:kv2v.0'
         res = aggregator.text(uri)
         assert res.status_code == 200
-        assert res.text.startswith('Lewis Carroll\nAlice\'s Abenteuer im Wunderland')
+        assert res.text.startswith("Lewis Carroll\nAlice's Abenteuer im Wunderland")
         assert res.text.endswith('im Haus seiner Schwester bei Guildford und wird dort beerdigt.\n')
 
     @staticmethod
     def test_more_text_uri_list(aggregator):
-        """get plain text for two uris (as list) # noqa: DAR101
-        """
+        """get plain text for two uris (as list) # noqa: DAR101"""
         uri = ['textgrid:kv2v.0', 'textgrid:kv2q.0']
         res = aggregator.text(uri)
         assert res.status_code == 200
@@ -66,38 +58,34 @@ class TestAggregatorIntegration:
 
     @staticmethod
     def test_more_text_uri_list_different_order(aggregator):
-        """get plain text for two uris (as list) in reverse order # noqa: DAR101
-        """
+        """get plain text for two uris (as list) in reverse order # noqa: DAR101"""
         uri = ['textgrid:kv2q.0', 'textgrid:kv2v.0']
         res = aggregator.text(uri)
         assert res.status_code == 200
-        assert res.text.startswith('Lewis Carroll\nAlice\'s Abenteuer im Wunderland')
+        assert res.text.startswith("Lewis Carroll\nAlice's Abenteuer im Wunderland")
         assert res.text.endswith('im Haus seiner Schwester bei Guildford und wird dort beerdigt.\n')
 
     @staticmethod
     def test_single_teicorpus(aggregator):
-        """get teicorpus for single uri # noqa: DAR101
-        """
+        """get teicorpus for single uri # noqa: DAR101"""
         uri = 'textgrid:kv2q.0'
         res = aggregator.teicorpus(uri)
         assert res.status_code == 200
-        assert res.text.startswith('<?xml version=\'1.0\' encoding=\'UTF-8\'?><TEI')
+        assert res.text.startswith("<?xml version='1.0' encoding='UTF-8'?><TEI")
         assert res.text.endswith('</TEI>')
 
     @staticmethod
     def test_teicorpus_uri_list(aggregator):
-        """get teicorpus for two uris (as list) # noqa: DAR101
-        """
+        """get teicorpus for two uris (as list) # noqa: DAR101"""
         uri = ['textgrid:kv2v.0', 'textgrid:kv2q.0']
         res = aggregator.teicorpus(uri)
         assert res.status_code == 200
-        assert res.text.startswith('<?xml version=\'1.0\' encoding=\'UTF-8\'?><teiCorpus')
+        assert res.text.startswith("<?xml version='1.0' encoding='UTF-8'?><teiCorpus")
         assert res.text.endswith('</TEI></teiCorpus>')
 
     @staticmethod
     def test_single_render(aggregator):
-        """get html for single uri # noqa: DAR101
-        """
+        """get html for single uri # noqa: DAR101"""
         uri = 'textgrid:kv2q.0'
         res = aggregator.render(uri)
         assert res.status_code == 200
@@ -106,8 +94,7 @@ class TestAggregatorIntegration:
 
     @staticmethod
     def test_render_urilist(aggregator):
-        """get html for two uris # noqa: DAR101
-        """
+        """get html for two uris # noqa: DAR101"""
         uri = ['textgrid:kv2v.0', 'textgrid:kv2q.0']
         res = aggregator.render(uri)
         assert res.status_code == 200
@@ -117,8 +104,8 @@ class TestAggregatorIntegration:
     @staticmethod
     def test_single_render_stylesheet(aggregator):
         """test rendering for single uri
-           with own stylesheet.
-           textgrid:4228q.2 counts words # noqa: DAR101
+        with own stylesheet.
+        textgrid:4228q.2 counts words # noqa: DAR101
         """
         uri = 'textgrid:kv2q.0'
         res = aggregator.render(uri, stylesheet_uri='textgrid:4228q.2', mediatype='text/plain')
@@ -128,8 +115,8 @@ class TestAggregatorIntegration:
     @staticmethod
     def test_render_urilist_stylesheet(aggregator):
         """test rendering for two uris
-           with own stylesheet.
-           textgrid:4228q.2 counts words # noqa: DAR101
+        with own stylesheet.
+        textgrid:4228q.2 counts words # noqa: DAR101
         """
         uri = ['textgrid:kv2v.0', 'textgrid:kv2q.0']
         res = aggregator.render(uri, stylesheet_uri='textgrid:4228q.2', mediatype='text/plain')
diff --git a/tests/integration/test_auth_integration.py b/tests/integration/test_auth_integration.py
index e868463971f833a346c0dfbc139a40a26e8c30c0..c81e2089d0870c973ef4e37017e6959358bdb138 100644
--- a/tests/integration/test_auth_integration.py
+++ b/tests/integration/test_auth_integration.py
@@ -11,7 +11,6 @@ from tgclients.auth import TextgridAuthException
 
 @pytest.mark.integration
 class TestTextgridAuthIntegration:
-
     @staticmethod
     def test_list_assigned_projects(auth):
         sid = os.getenv('SESSION_ID')
@@ -37,8 +36,7 @@ class TestTextgridAuthIntegration:
 
     @staticmethod
     def test_get_project_description(auth):
-        project_info = auth.get_project_description(
-            'TGPR-372fe6dc-57f2-6cd4-01b5-2c4bbefcfd3c')
+        project_info = auth.get_project_description('TGPR-372fe6dc-57f2-6cd4-01b5-2c4bbefcfd3c')
         assert project_info.name == 'Digitale Bibliothek'
 
     @staticmethod
@@ -46,7 +44,10 @@ class TestTextgridAuthIntegration:
         sid = os.getenv('SESSION_ID')
         now = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
         project_id = auth.create_project(
-            sid, 'Integration test project from tgclients ' + now, 'Integration test project from tgclients')
+            sid,
+            'Integration test project from tgclients ' + now,
+            'Integration test project from tgclients',
+        )
         assert len(project_id) > 1
         res = auth.delete_project(sid, project_id)
         assert res is True
@@ -62,8 +63,12 @@ class TestTextgridAuthIntegration:
     def test_create_without_valid_sid(auth):
         sid = 'no-valid-sid'
         with pytest.raises(TextgridAuthException):
-            auth.create_project(sid, 'Integration test project from tgclients',
-                                'Integration test project from tgclients', False)
+            auth.create_project(
+                sid,
+                'Integration test project from tgclients',
+                'Integration test project from tgclients',
+                False,
+            )
 
     @staticmethod
     def test_delete_without_valid_sid(auth):
diff --git a/tests/integration/test_crud_integration.py b/tests/integration/test_crud_integration.py
index a748172428053254b071c0e4e8bf6e222ba92782..0e1b39a35246fd7e44ba382bc4a5a81d038bec96 100644
--- a/tests/integration/test_crud_integration.py
+++ b/tests/integration/test_crud_integration.py
@@ -6,15 +6,14 @@ import os
 from datetime import datetime
 
 import pytest
-from tgclients.metadata import TextgridMetadata
 from tgclients.crud import TextgridCrudException
+from tgclients.metadata import TextgridMetadata
 
 FIXTURE_PATH = './tests/fixtures/'
 
 
 @pytest.mark.integration
 class TestTextgridCrudIntegration:
-
     @staticmethod
     def test_read_public_data(crud):
         uri = 'textgrid:kv2q.0'
@@ -46,8 +45,7 @@ class TestTextgridCrudIntegration:
         sid = os.getenv('SESSION_ID')
         project_id = os.getenv('PROJECT_ID')
         now = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
-        metadata = TextgridMetadata().build(
-            title='test '+now, mimetype='text/xml')
+        metadata = TextgridMetadata().build(title='test ' + now, mimetype='text/xml')
         data = '<content>here</content>'
 
         res = crud.create_resource(sid, project_id, data, metadata)
@@ -61,8 +59,7 @@ class TestTextgridCrudIntegration:
         sid = os.getenv('SESSION_ID')
         project_id = os.getenv('PROJECT_ID')
         now = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
-        metadata = TextgridMetadata().build(
-            title='test '+now, mimetype='text/xml')
+        metadata = TextgridMetadata().build(title='test ' + now, mimetype='text/xml')
         data = '<content>here</content>'
 
         md_rev0 = crud.create_resource(sid, project_id, data, metadata)
@@ -77,8 +74,7 @@ class TestTextgridCrudIntegration:
         assert textgrid_rev1_uri.endswith('.1')
         assert md_rev1.object_value.generic.generated.revision == 1
 
-        md_rev2 = crud.update_resource(
-            sid, textgrid_rev1_uri, data, md_rev1, create_revision=True)
+        md_rev2 = crud.update_resource(sid, textgrid_rev1_uri, data, md_rev1, create_revision=True)
         textgrid_rev2_uri = md_rev2.object_value.generic.generated.textgrid_uri.value
         assert textgrid_rev2_uri.startswith('textgrid:')
         assert textgrid_rev2_uri.endswith('.2')
@@ -99,8 +95,7 @@ class TestTextgridCrudIntegration:
         project_id = os.getenv('PROJECT_ID')
         now = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
         title = 'test ' + now
-        metadata = TextgridMetadata().build(
-            title=title, mimetype='text/xml')
+        metadata = TextgridMetadata().build(title=title, mimetype='text/xml')
         data = '<content>here</content>'
 
         cmeta = crud.create_resource(sid, project_id, data, metadata)
@@ -122,8 +117,7 @@ class TestTextgridCrudIntegration:
         sid = os.getenv('SESSION_ID')
         project_id = os.getenv('PROJECT_ID')
         now = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
-        metadata = TextgridMetadata().build(
-            title='test '+now, mimetype='text/plain')
+        metadata = TextgridMetadata().build(title='test ' + now, mimetype='text/plain')
         # the content on creation
         cdata = 'created'
 
@@ -138,8 +132,7 @@ class TestTextgridCrudIntegration:
         umetadata = crud.read_metadata(textgrid_uri, sid)
         # updated content
         udata = 'updated'
-        res = crud.update_resource(
-            sid, textgrid_uri, udata, umetadata)
+        res = crud.update_resource(sid, textgrid_uri, udata, umetadata)
         textgrid_uri = res.object_value.generic.generated.textgrid_uri.value
         assert textgrid_uri.startswith('textgrid:')
 
@@ -155,8 +148,7 @@ class TestTextgridCrudIntegration:
         sid = os.getenv('SESSION_ID')
         project_id = os.getenv('PROJECT_ID')
         now = datetime.now().strftime('%Y-%m-%d-%H_M_%S')
-        metadata = TextgridMetadata().build(
-            title='stream-test '+now, mimetype='image/png')
+        metadata = TextgridMetadata().build(title='stream-test ' + now, mimetype='image/png')
 
         ###
         # the content on creation
@@ -164,8 +156,7 @@ class TestTextgridCrudIntegration:
         cfile_loc = FIXTURE_PATH + 'eulen150dpi.png'
         csize = os.path.getsize(cfile_loc)
         with open(cfile_loc, 'rb') as cdata:
-            res = crud.create_resource(
-                sid, project_id, cdata, metadata)
+            res = crud.create_resource(sid, project_id, cdata, metadata)
 
         textgrid_uri = res.object_value.generic.generated.textgrid_uri.value
         assert textgrid_uri.startswith('textgrid:')
@@ -188,8 +179,7 @@ class TestTextgridCrudIntegration:
         ufile_loc = FIXTURE_PATH + 'eulen300dpi.png'
         usize = os.path.getsize(ufile_loc)
         with open(ufile_loc, 'rb') as udata:
-            res = crud.update_resource(
-                sid, textgrid_uri, udata, umetadata)
+            res = crud.update_resource(sid, textgrid_uri, udata, umetadata)
 
         textgrid_uri = res.object_value.generic.generated.textgrid_uri.value
         assert textgrid_uri.startswith('textgrid:')
diff --git a/tests/integration/test_crud_request_integration.py b/tests/integration/test_crud_request_integration.py
index 5ca0a6d6ff25963b37a88570705c86e911865c15..4ca96dbb8af1326f4135961c2b9bab84ab8bafb7 100644
--- a/tests/integration/test_crud_request_integration.py
+++ b/tests/integration/test_crud_request_integration.py
@@ -13,7 +13,6 @@ FIXTURE_PATH = './tests/fixtures/'
 
 @pytest.mark.integration
 class TestTextgridCrudIntegration:
-
     @staticmethod
     def test_read_public_data(crud_request):
         uri = 'textgrid:kv2q.0'
@@ -37,8 +36,7 @@ class TestTextgridCrudIntegration:
         sid = os.getenv('SESSION_ID')
         project_id = os.getenv('PROJECT_ID')
         now = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
-        metadata = TextgridMetadata.create(
-            title='test '+now, mimetype='text/xml')
+        metadata = TextgridMetadata.create(title='test ' + now, mimetype='text/xml')
         data = '<content>here</content>'
 
         res = crud_request.create_resource(sid, project_id, data, metadata)
@@ -53,8 +51,7 @@ class TestTextgridCrudIntegration:
         sid = os.getenv('SESSION_ID')
         project_id = os.getenv('PROJECT_ID')
         now = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
-        metadata = TextgridMetadata().create(
-            title='test '+now, mimetype='text/xml')
+        metadata = TextgridMetadata().create(title='test ' + now, mimetype='text/xml')
         data = '<content>here</content>'
 
         res_rev0 = crud_request.create_resource(sid, project_id, data, metadata)
@@ -71,9 +68,9 @@ class TestTextgridCrudIntegration:
         assert textgrid_rev1_uri.startswith('textgrid:')
         assert textgrid_rev1_uri.endswith('.1')
 
-
         res_rev2 = crud_request.update_resource(
-            sid, textgrid_rev1_uri, data, md_rev1, create_revision=True)
+            sid, textgrid_rev1_uri, data, md_rev1, create_revision=True
+        )
         assert res_rev2.status_code == 200
         textgrid_rev2_uri = res_rev2.headers['Location']
         assert textgrid_rev2_uri.startswith('textgrid:')
@@ -93,8 +90,7 @@ class TestTextgridCrudIntegration:
         sid = os.getenv('SESSION_ID')
         project_id = os.getenv('PROJECT_ID')
         now = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
-        metadata = TextgridMetadata.create(
-            title='test '+now, mimetype='text/plain')
+        metadata = TextgridMetadata.create(title='test ' + now, mimetype='text/plain')
         # the content on creation
         cdata = 'created'
 
@@ -126,13 +122,12 @@ class TestTextgridCrudIntegration:
         sid = os.getenv('SESSION_ID')
         project_id = os.getenv('PROJECT_ID')
         now = datetime.now().strftime('%Y-%m-%d-%H_M_%S')
-        metadata = TextgridMetadata.create(
-            title='stream-test '+now, mimetype='image/png')
+        metadata = TextgridMetadata.create(title='stream-test ' + now, mimetype='image/png')
 
         ###
         # the content on creation
         ###
-        cfile_loc =  FIXTURE_PATH + 'eulen150dpi.png'
+        cfile_loc = FIXTURE_PATH + 'eulen150dpi.png'
         csize = os.path.getsize(cfile_loc)
         with open(cfile_loc, 'rb') as cdata:
             res = crud_request.create_resource(sid, project_id, cdata, metadata)
@@ -141,7 +136,7 @@ class TestTextgridCrudIntegration:
         assert res.headers['Location'].startswith('textgrid:')
         textgrid_uri = res.headers['Location']
 
-        dl1_png_loc = '/tmp/unit-'+now+'-1.png'
+        dl1_png_loc = '/tmp/unit-' + now + '-1.png'
         res = crud_request.read_data(textgrid_uri, sid)
         assert res.status_code == 200
 
@@ -156,7 +151,7 @@ class TestTextgridCrudIntegration:
         ###
         # metadata for update needs to have correct last-modified date
         umetadata = str(crud_request.read_metadata(textgrid_uri, sid).content, 'utf-8')
-        ufile_loc =  FIXTURE_PATH + 'eulen300dpi.png'
+        ufile_loc = FIXTURE_PATH + 'eulen300dpi.png'
         usize = os.path.getsize(ufile_loc)
         with open(ufile_loc, 'rb') as udata:
             res = crud_request.update_resource(sid, textgrid_uri, udata, umetadata)
@@ -164,7 +159,7 @@ class TestTextgridCrudIntegration:
         assert res.status_code == 200
         assert res.headers['Location'].startswith('textgrid:')
 
-        dl2_png_loc = '/tmp/unit-'+now+'-2.png'
+        dl2_png_loc = '/tmp/unit-' + now + '-2.png'
         res = crud_request.read_data(textgrid_uri, sid)
         assert res.status_code == 200
 
diff --git a/tests/integration/test_notebooks.py b/tests/integration/test_notebooks.py
index e946775f72566dec3898b7ec76f87c062b0c892a..81f6bf66ff5ba9bcbf7d1631d46da466516f69e2 100644
--- a/tests/integration/test_notebooks.py
+++ b/tests/integration/test_notebooks.py
@@ -9,7 +9,6 @@ from testbook import testbook
 
 @pytest.mark.integration
 class TestNotebooks:
-
     @staticmethod
     @testbook('notebooks/Kallimachos Import.ipynb')
     def test_notebook(tb, crud, auth, tgconfig):
@@ -19,13 +18,13 @@ class TestNotebooks:
 
         sid = os.getenv('SESSION_ID')
         host = tgconfig.host
-        tb.inject(f'''
+        tb.inject(f"""
             SID = "{sid}"
             TG_HOST = "{host}"
 
             from tgclients.config import TextgridConfig
             config = TextgridConfig(TG_HOST)
-        ''')
+        """)
         tb.execute_cell('create-project')
         project_id = tb.ref('project_id')
         assert project_id.startswith('TGPR-')
@@ -35,8 +34,9 @@ class TestNotebooks:
         assert collection_uri.startswith('textgrid:')
         assert collection_uri.endswith('.0')
 
-        res = tb.execute_cell('list-collection')
+        tb.execute_cell('list-collection')
         # this test should work again when rdf4j federated repo is fixed
+        # res = tb.execute_cell('list-collection')
         # assert 'Die Rumplhanni' in res['outputs'][0]['text']
 
         # cleanup
diff --git a/tests/integration/test_textgrid_search_integration.py b/tests/integration/test_textgrid_search_integration.py
index 216c6bcc18f135bcf64d1d67cd5e424635bad25b..c93b00f3b0bf3a08650e4e09b317999987aeb603 100644
--- a/tests/integration/test_textgrid_search_integration.py
+++ b/tests/integration/test_textgrid_search_integration.py
@@ -3,12 +3,12 @@
 # SPDX-License-Identifier: LGPL-3.0-or-later
 
 import pytest
-from tgclients.databinding.tgsearch import Response as SearchResponse, TextgridUris
+from tgclients.databinding.tgsearch import Response as SearchResponse
+from tgclients.databinding.tgsearch import TextgridUris
 
 
 @pytest.mark.integration
 class TestTextgridSearchIntegration:
-
     @staticmethod
     def test_public_info(search_public):
         uri = 'textgrid:kv2q.0'
@@ -38,8 +38,7 @@ class TestTextgridSearchIntegration:
     @staticmethod
     def test_public_search_filter_format_project(search_public):
         pid = 'TGPR-372fe6dc-57f2-6cd4-01b5-2c4bbefcfd3c'
-        filters = ['format:text/xml',
-                   'project.id:'+pid]
+        filters = ['format:text/xml', 'project.id:' + pid]
         res: SearchResponse = search_public.search(filters=filters)
         assert int(res.hits) > 100
         for r in res.result:
diff --git a/tests/integration/test_textgrid_search_request_integration.py b/tests/integration/test_textgrid_search_request_integration.py
index 337efb0c470f4e9f547d3962768eddb4d0f1199a..e591893aff8d4585f39b6a3f4afa62a46912dc26 100644
--- a/tests/integration/test_textgrid_search_request_integration.py
+++ b/tests/integration/test_textgrid_search_request_integration.py
@@ -7,7 +7,6 @@ import pytest
 
 @pytest.mark.integration
 class TestTextgridSearchRequestIntegration:
-
     @staticmethod
     def test_public_info(searchrequest_public):
         uri = 'textgrid:kv2q.0'
diff --git a/tests/integration/test_textgrid_utils.py b/tests/integration/test_textgrid_utils.py
index 79dc0e07ce64f1343e96e683562a7a98abf70891..a8a3775dc69828efe54b1b846147ce5e3be96ff5 100644
--- a/tests/integration/test_textgrid_utils.py
+++ b/tests/integration/test_textgrid_utils.py
@@ -2,12 +2,11 @@
 #
 # SPDX-License-Identifier: LGPL-3.0-or-later
 
-from tgclients.utils import Utils
 
 def test_public_aggregation_to_list(search_public):
     uri = 'textgrid:kv2p.0'
     res = search_public.list_aggregation(uri)
-    #assert res.status_code == 200
+    # assert res.status_code == 200
     urilist = [res.textgrid_uri for res in res.result]
     print(urilist)
-    assert(urilist == ['textgrid:kv2x.0', 'textgrid:kv2t.0'])
+    assert urilist == ['textgrid:kv2x.0', 'textgrid:kv2t.0']
diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py
index 694750595de71da0c6e63fc39e1b57ca6b456871..9033864d9dffd8b8f256716089b6a6e4db2a600f 100644
--- a/tests/unit/__init__.py
+++ b/tests/unit/__init__.py
@@ -4,6 +4,7 @@
 
 FIXTURE_PATH = './tests/fixtures/'
 
+
 def fileLoader(filename):
     def loader(req, context):
         with open(FIXTURE_PATH + filename, 'r') as f:
@@ -11,6 +12,7 @@ def fileLoader(filename):
 
     return loader
 
+
 def binaryFileLoader(filename):
     def loader(req, context):
         with open(FIXTURE_PATH + filename, 'rb') as f:
diff --git a/tests/unit/test_aggregatgor.py b/tests/unit/test_aggregatgor.py
index 788379d40e47a51fec60a15c85a02931fe6316e5..d4c05400273ca84420e2cac923c8a0cb83ce132d 100644
--- a/tests/unit/test_aggregatgor.py
+++ b/tests/unit/test_aggregatgor.py
@@ -1,55 +1,53 @@
 # SPDX-FileCopyrightText: 2022 Georg-August-Universität Göttingen
 #
 # SPDX-License-Identifier: LGPL-3.0-or-later
-import pytest
 
-from . import fileLoader, binaryFileLoader
+from . import binaryFileLoader, fileLoader
 
 
 class TestAggregator:
-
     @staticmethod
     def test_zip_single_uri(requests_mock, aggregator, tgconfig):
-        """ get a zip file for a single uri should be succesfull # noqa: DAR101
-        """
+        """get a zip file for a single uri should be succesfull # noqa: DAR101"""
         requests_mock.get(
             tgconfig.host + '/1.0/aggregator/zip/textgrid:kv2q.0',
-            content=binaryFileLoader('aggregator/zip_kv2q.0'))
+            content=binaryFileLoader('aggregator/zip_kv2q.0'),
+        )
         uri = 'textgrid:kv2q.0'
         res = aggregator.zip(uri)
         assert res.status_code == 200
 
     @staticmethod
     def test_zip_urilist(requests_mock, aggregator, tgconfig):
-        """ get a zip file for a single uri should be succesfull # noqa: DAR101
-        """
+        """get a zip file for a single uri should be succesfull # noqa: DAR101"""
         requests_mock.get(
             tgconfig.host + '/1.0/aggregator/zip/textgrid:kv2v.0,textgrid:kv2q.0',
-            content=binaryFileLoader('aggregator/zip_kv2v.0_kv2q.0'))
+            content=binaryFileLoader('aggregator/zip_kv2v.0_kv2q.0'),
+        )
         uris = ['textgrid:kv2v.0', 'textgrid:kv2q.0']
         res = aggregator.zip(uris)
         assert res.status_code == 200
 
     @staticmethod
     def test_single_text(requests_mock, aggregator, tgconfig):
-        """get plain text for single uri # noqa: DAR101
-        """
+        """get plain text for single uri # noqa: DAR101"""
         requests_mock.get(
             tgconfig.host + '/1.0/aggregator/text/textgrid:kv2q.0',
-            text=fileLoader('aggregator/text_kv2q.0'))
+            text=fileLoader('aggregator/text_kv2q.0'),
+        )
         uri = 'textgrid:kv2q.0'
         res = aggregator.text(uri)
         assert res.status_code == 200
-        assert res.text.startswith('Lewis Carroll\nAlice\'s Abenteuer im Wunderland')
+        assert res.text.startswith("Lewis Carroll\nAlice's Abenteuer im Wunderland")
         assert res.text.endswith('an ihr eigenes Kindesleben und die glücklichen Sommertage.\n')
 
     @staticmethod
     def test_more_text_uri_string(requests_mock, aggregator, tgconfig):
-        """get plain text for two uris (as string) # noqa: DAR101
-        """
+        """get plain text for two uris (as string) # noqa: DAR101"""
         requests_mock.get(
             tgconfig.host + '/1.0/aggregator/text/textgrid:kv2v.0,textgrid:kv2q.0',
-            text=fileLoader('aggregator/text_kv2v.0_kv2q.0'))
+            text=fileLoader('aggregator/text_kv2v.0_kv2q.0'),
+        )
         uri = 'textgrid:kv2v.0,textgrid:kv2q.0'
         res = aggregator.text(uri)
         assert res.status_code == 200
@@ -58,24 +56,24 @@ class TestAggregator:
 
     @staticmethod
     def test_more_text_uri_string_different_order(requests_mock, aggregator, tgconfig):
-        """get plain text for two uris (as string) in reverse order # noqa: DAR101
-        """
+        """get plain text for two uris (as string) in reverse order # noqa: DAR101"""
         requests_mock.get(
             tgconfig.host + '/1.0/aggregator/text/textgrid:kv2q.0,textgrid:kv2v.0',
-            text=fileLoader('aggregator/text_kv2q.0_kv2v.0'))
+            text=fileLoader('aggregator/text_kv2q.0_kv2v.0'),
+        )
         uri = 'textgrid:kv2q.0,textgrid:kv2v.0'
         res = aggregator.text(uri)
         assert res.status_code == 200
-        assert res.text.startswith('Lewis Carroll\nAlice\'s Abenteuer im Wunderland')
+        assert res.text.startswith("Lewis Carroll\nAlice's Abenteuer im Wunderland")
         assert res.text.endswith('im Haus seiner Schwester bei Guildford und wird dort beerdigt.\n')
 
     @staticmethod
     def test_more_text_uri_list(requests_mock, aggregator, tgconfig):
-        """get plain text for two uris (as list) # noqa: DAR101
-        """
+        """get plain text for two uris (as list) # noqa: DAR101"""
         requests_mock.get(
             tgconfig.host + '/1.0/aggregator/text/textgrid:kv2v.0,textgrid:kv2q.0',
-            text=fileLoader('aggregator/text_kv2v.0_kv2q.0'))
+            text=fileLoader('aggregator/text_kv2v.0_kv2q.0'),
+        )
         uri = ['textgrid:kv2v.0', 'textgrid:kv2q.0']
         res = aggregator.text(uri)
         assert res.status_code == 200
@@ -84,52 +82,54 @@ class TestAggregator:
 
     @staticmethod
     def test_more_text_uri_list_different_order(requests_mock, aggregator, tgconfig):
-        """get plain text for two uris (as list) in reverse order # noqa: DAR101
-        """
+        """get plain text for two uris (as list) in reverse order # noqa: DAR101"""
         requests_mock.get(
             tgconfig.host + '/1.0/aggregator/text/textgrid:kv2q.0,textgrid:kv2v.0',
-            text=fileLoader('aggregator/text_kv2q.0_kv2v.0'))
+            text=fileLoader('aggregator/text_kv2q.0_kv2v.0'),
+        )
         uri = ['textgrid:kv2q.0', 'textgrid:kv2v.0']
         res = aggregator.text(uri)
         assert res.status_code == 200
-        assert res.text.startswith('Lewis Carroll\nAlice\'s Abenteuer im Wunderland')
+        assert res.text.startswith("Lewis Carroll\nAlice's Abenteuer im Wunderland")
         assert res.text.endswith('im Haus seiner Schwester bei Guildford und wird dort beerdigt.\n')
 
     @staticmethod
     def test_single_teicorpus(requests_mock, aggregator, tgconfig):
-        """get teicorpus for single uri # noqa: DAR101
-        """
+        """get teicorpus for single uri # noqa: DAR101"""
         requests_mock.get(
             tgconfig.host + '/1.0/aggregator/teicorpus/textgrid:kv2q.0',
-            text=fileLoader('aggregator/teicorpus_kv2q.0'))
+            text=fileLoader('aggregator/teicorpus_kv2q.0'),
+        )
         uri = 'textgrid:kv2q.0'
         res = aggregator.teicorpus(uri)
         assert res.status_code == 200
-        assert res.text.startswith('<?xml version=\'1.0\' encoding=\'UTF-8\'?><TEI')
+        assert res.text.startswith("<?xml version='1.0' encoding='UTF-8'?><TEI")
         assert res.text.endswith('</TEI>')
 
     @staticmethod
     def test_teicorpus_uri_list(requests_mock, aggregator, tgconfig):
-        """get teicorpus for two uris (as list) # noqa: DAR101
-        """
+        """get teicorpus for two uris (as list) # noqa: DAR101"""
         requests_mock.get(
             tgconfig.host + '/1.0/aggregator/teicorpus/textgrid:kv2v.0,textgrid:kv2q.0',
-            text=fileLoader('aggregator/teicorpus_kv2v.0_kv2q.0'))
+            text=fileLoader('aggregator/teicorpus_kv2v.0_kv2q.0'),
+        )
         uri = ['textgrid:kv2v.0', 'textgrid:kv2q.0']
         res = aggregator.teicorpus(uri)
         assert res.status_code == 200
-        assert res.text.startswith('<?xml version=\'1.0\' encoding=\'UTF-8\'?><teiCorpus')
+        assert res.text.startswith("<?xml version='1.0' encoding='UTF-8'?><teiCorpus")
         assert res.text.endswith('</TEI></teiCorpus>')
 
     @staticmethod
     def test_single_render_stylesheet(requests_mock, aggregator, tgconfig):
         """test rendering for single uri
-           with own stylesheet.
-           textgrid:4228q.2 counts words # noqa: DAR101
+        with own stylesheet.
+        textgrid:4228q.2 counts words # noqa: DAR101
         """
         requests_mock.get(
-            tgconfig.host + '/1.0/aggregator/html/textgrid:kv2q.0?stylesheet=textgrid%3A4228q.2&mediatype=text%2Fplain',
-            text='25598')
+            tgconfig.host
+            + '/1.0/aggregator/html/textgrid:kv2q.0?stylesheet=textgrid%3A4228q.2&mediatype=text%2Fplain',
+            text='25598',
+        )
         uri = 'textgrid:kv2q.0'
         res = aggregator.render(uri, stylesheet_uri='textgrid:4228q.2', mediatype='text/plain')
         assert res.status_code == 200
@@ -138,12 +138,14 @@ class TestAggregator:
     @staticmethod
     def test_render_urilist_stylesheet(requests_mock, aggregator, tgconfig):
         """test rendering for two uris
-           with own stylesheet.
-           textgrid:4228q.2 counts words # noqa: DAR101
+        with own stylesheet.
+        textgrid:4228q.2 counts words # noqa: DAR101
         """
         requests_mock.get(
-            tgconfig.host + '/1.0/aggregator/html/textgrid:kv2v.0,textgrid:kv2q.0?stylesheet=textgrid%3A4228q.2&mediatype=text%2Fplain',
-            text='26311')
+            tgconfig.host
+            + '/1.0/aggregator/html/textgrid:kv2v.0,textgrid:kv2q.0?stylesheet=textgrid%3A4228q.2&mediatype=text%2Fplain',
+            text='26311',
+        )
         uri = ['textgrid:kv2v.0', 'textgrid:kv2q.0']
         res = aggregator.render(uri, stylesheet_uri='textgrid:4228q.2', mediatype='text/plain')
         assert res.status_code == 200
diff --git a/tests/unit/test_auth.py b/tests/unit/test_auth.py
index e12cb7af53c84cdbba64b119848dbc2cb3721c33..3fcb22090b1aefb67b4e01321ecc105ce7bcdfa2 100644
--- a/tests/unit/test_auth.py
+++ b/tests/unit/test_auth.py
@@ -4,8 +4,9 @@
 
 from tgclients.auth import TextgridAuth
 
+
 def test_empty_constructor():
     """Not passing a config in constructor should provide a client for
-       the prodction system textgridlab.org"""
+    the prodction system textgridlab.org"""
     tga = TextgridAuth()
-    assert tga._config.auth_address.startswith("https://textgridlab.org/1.0/")
+    assert tga._config.auth_address.startswith('https://textgridlab.org/1.0/')
diff --git a/tests/unit/test_crud_request.py b/tests/unit/test_crud_request.py
index da706a4bc0c8f1a009d23a4fe49826cedc439e9d..82a2bee362b478913bf7dacd9cbeef6898d9365a 100644
--- a/tests/unit/test_crud_request.py
+++ b/tests/unit/test_crud_request.py
@@ -4,30 +4,29 @@
 
 from datetime import datetime
 
-import pytest
 from tgclients.metadata import TextgridMetadata
-from tgclients.config import TextgridConfig
-from tgclients.crud import TextgridCrud, TextgridCrudException
 
-from . import fileLoader, binaryFileLoader
+from . import binaryFileLoader
 
 
 class TestTextgridCrudRequest:
-
     @staticmethod
     def test_create_revision_and_delete(requests_mock, crud_request, tgconfig):
         requests_mock.post(
-            tgconfig.host + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&projectId=PROJECT_ID&createRevision=false',
+            tgconfig.host
+            + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&projectId=PROJECT_ID&createRevision=false',
             headers={'Location': 'textgrid:kv2q.0'},
             content=binaryFileLoader('crud/kv2q.0.metadata'),
         )
         requests_mock.post(
-            tgconfig.host + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&uri=textgrid%3Akv2q.0&createRevision=true&projectId=PROJECT_ID',
+            tgconfig.host
+            + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&uri=textgrid%3Akv2q.0&createRevision=true&projectId=PROJECT_ID',
             headers={'Location': 'textgrid:kv2q.1'},
             content=binaryFileLoader('crud/kv2q.1.metadata'),
         )
         requests_mock.post(
-            tgconfig.host + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&uri=textgrid%3Akv2q.1&createRevision=true&projectId=PROJECT_ID',
+            tgconfig.host
+            + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&uri=textgrid%3Akv2q.1&createRevision=true&projectId=PROJECT_ID',
             headers={'Location': 'textgrid:kv2q.2'},
             content=binaryFileLoader('crud/kv2q.2.metadata'),
         )
@@ -46,8 +45,7 @@ class TestTextgridCrudRequest:
         sid = 'SESSION_ID'  # does not matter for mocked requests
         project_id = 'PROJECT_ID'  # does not matter for mocked requests
         now = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
-        metadata = TextgridMetadata().create(
-            title='test '+now, mimetype='text/xml')
+        metadata = TextgridMetadata().create(title='test ' + now, mimetype='text/xml')
         data = '<content>here</content>'
 
         res_rev0 = crud_request.create_resource(sid, project_id, data, metadata)
@@ -65,7 +63,8 @@ class TestTextgridCrudRequest:
         assert textgrid_rev1_uri.endswith('.1')
 
         res_rev2 = crud_request.update_resource(
-            sid, textgrid_rev1_uri, data, md_rev1, create_revision=True)
+            sid, textgrid_rev1_uri, data, md_rev1, create_revision=True
+        )
         assert res_rev2.status_code == 200
         textgrid_rev2_uri = res_rev2.headers['Location']
         assert textgrid_rev2_uri.startswith('textgrid:')
@@ -82,25 +81,25 @@ class TestTextgridCrudRequest:
 
     @staticmethod
     def test_error_msg_from_html(crud_request):
-        html = '''
+        html = """
                <html>
                    <head>
                        <meta name="description" content="blubber">
                    </head>
                </html>
-               '''
+               """
 
         msg = crud_request._error_msg_from_html(html)
-        assert msg == "blubber"
+        assert msg == 'blubber'
 
     @staticmethod
     def test_error_msg_from_html_no_meta(crud_request):
-        html = '''<html>
+        html = """<html>
                       <head>
                          <title>look, error with no meta tag</title>
                      </head>
                   </html>
-               '''
+               """
 
         msg = crud_request._error_msg_from_html(html)
         assert msg.startswith('<html>')
diff --git a/tests/unit/test_textgrid_config.py b/tests/unit/test_textgrid_config.py
index 99e690334845d617cac253a3c54f75ead0a33636..90d412a1f5ba15e7b26dd7522a8aafcc97fe391d 100644
--- a/tests/unit/test_textgrid_config.py
+++ b/tests/unit/test_textgrid_config.py
@@ -2,57 +2,59 @@
 #
 # SPDX-License-Identifier: LGPL-3.0-or-later
 
-from tgclients.config import TextgridConfig, DEFAULT_TIMEOUT, DEV_SERVER, TEST_SERVER
+from tgclients.config import DEFAULT_TIMEOUT, DEV_SERVER, TEST_SERVER, TextgridConfig
+
 
 def test_default_config():
-    """ default empty constructor should be configured for textgrid production server
-    """
+    """default empty constructor should be configured for textgrid production server"""
     config = TextgridConfig()
     assert config.host == 'https://textgridlab.org'
     assert config.search == 'https://textgridlab.org/1.0/tgsearch'
 
+
 def test_dev_config():
-    """ constructor should overwrite textgrid host
-    """
+    """constructor should overwrite textgrid host"""
     config = TextgridConfig('https://dev.textgridlab.org')
     assert config.host == 'https://dev.textgridlab.org'
     assert config.search == 'https://dev.textgridlab.org/1.0/tgsearch'
 
+
 def test_dev_config_from_const():
-    """ constructor should overwrite textgrid host
-    """
+    """constructor should overwrite textgrid host"""
     config = TextgridConfig(DEV_SERVER)
     assert config.host == 'https://dev.textgridlab.org'
     assert config.search == 'https://dev.textgridlab.org/1.0/tgsearch'
 
+
 def test_test_config_from_const():
-    """ constructor should overwrite textgrid host
-    """
+    """constructor should overwrite textgrid host"""
     config = TextgridConfig(TEST_SERVER)
     assert config.host == 'https://test.textgridlab.org'
     assert config.search == 'https://test.textgridlab.org/1.0/tgsearch'
 
+
 def test_host_url_trailing_slash():
-    """ constructor should remove trailing slashes
-    """
+    """constructor should remove trailing slashes"""
     config = TextgridConfig('https://dev.textgridlab.org/')
     assert config.host == 'https://dev.textgridlab.org'
     assert config.search == 'https://dev.textgridlab.org/1.0/tgsearch'
 
+
 def test_get_set_http_timeout():
-    """ default timeout should be overwritten by setter
-    """
+    """default timeout should be overwritten by setter"""
     config = TextgridConfig()
     assert config.http_timeout == DEFAULT_TIMEOUT
     new_timeout: float = 30.2
     config.http_timeout = new_timeout
     assert config.http_timeout is new_timeout
 
+
 def test_default_for_none():
     config = TextgridConfig(None)
     assert config.host == 'https://textgridlab.org'
     assert config.search == 'https://textgridlab.org/1.0/tgsearch'
 
+
 def test_default_for_empty():
     config = TextgridConfig('')
     assert config.host == 'https://textgridlab.org'
diff --git a/tests/unit/test_textgrid_crud.py b/tests/unit/test_textgrid_crud.py
index d6d6156e92b070774ac2242bd3f267b14b7af0bb..7192bd7f65bc895fe9b253562ae8828b339422ca 100644
--- a/tests/unit/test_textgrid_crud.py
+++ b/tests/unit/test_textgrid_crud.py
@@ -5,19 +5,18 @@
 from datetime import datetime
 
 import pytest
-from tgclients.metadata import TextgridMetadata
 from tgclients.config import TextgridConfig
 from tgclients.crud import TextgridCrud, TextgridCrudException
+from tgclients.metadata import TextgridMetadata
 
-from . import fileLoader, binaryFileLoader
+from . import binaryFileLoader
 
 
 class TestTextgridCrud:
-
     @staticmethod
     def test_empty_constructor():
         """Not passing a config in constructor should provide a client for
-            tgcrud nonpublic on production system"""
+        tgcrud nonpublic on production system"""
         tgc = TextgridCrud()
         assert tgc._url == TextgridConfig().crud
 
@@ -25,7 +24,8 @@ class TestTextgridCrud:
     def test_read_public_data(requests_mock, crud, tgconfig):
         requests_mock.get(
             tgconfig.host + '/1.0/tgcrud/rest/textgrid:kv2q.0/data',
-            content=binaryFileLoader('crud/kv2q.0.data'))
+            content=binaryFileLoader('crud/kv2q.0.data'),
+        )
         uri = 'textgrid:kv2q.0'
         res = crud.read_data(uri)
         assert 'Alice fing an sich zu langweilen;' in str(res.content, 'utf-8')
@@ -34,7 +34,8 @@ class TestTextgridCrud:
     def test_read_public_metadata(requests_mock, crud, tgconfig):
         requests_mock.get(
             tgconfig.host + '/1.0/tgcrud/rest/textgrid:kv2q.0/metadata',
-            content=binaryFileLoader('crud/kv2q.0.metadata'))
+            content=binaryFileLoader('crud/kv2q.0.metadata'),
+        )
         uri = 'textgrid:kv2q.0'
         res = crud.read_metadata(uri)
         title = res.object_value.generic.provided.title[0]
@@ -44,7 +45,8 @@ class TestTextgridCrud:
     def test_read_public_metadata_umlaut(requests_mock, crud, tgconfig):
         requests_mock.get(
             tgconfig.host + '/1.0/tgcrud/rest/textgrid:s60f.0/metadata',
-            content=binaryFileLoader('crud/s60f.0.metadata'))
+            content=binaryFileLoader('crud/s60f.0.metadata'),
+        )
         uri = 'textgrid:s60f.0'
         res = crud.read_metadata(uri)
         title = res.object_value.generic.provided.title[0]
@@ -55,7 +57,7 @@ class TestTextgridCrud:
         requests_mock.get(
             tgconfig.host + '/1.0/tgcrud/rest/textgrid:notexistent/metadata',
             status_code=404,
-            text='<meta name="description" content="wrong uri mock message">'
+            text='<meta name="description" content="wrong uri mock message">',
         )
         uri = 'textgrid:notexistent'
         with pytest.raises(TextgridCrudException):
@@ -64,7 +66,8 @@ class TestTextgridCrud:
     @staticmethod
     def test_create_and_delete(requests_mock, crud, tgconfig):
         requests_mock.post(
-            tgconfig.host + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&projectId=PROJECT_ID&createRevision=false',
+            tgconfig.host
+            + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&projectId=PROJECT_ID&createRevision=false',
             headers={'Location': 'textgrid:mocked'},
             content=binaryFileLoader('crud/kv2q.0.metadata'),
         )
@@ -75,8 +78,7 @@ class TestTextgridCrud:
         sid = 'SESSION_ID'  # does not matter for mocked requests
         project_id = 'PROJECT_ID'  # does not matter for mocked requests
         now = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
-        metadata = TextgridMetadata().build(
-            title='test '+now, mimetype='text/xml')
+        metadata = TextgridMetadata().build(title='test ' + now, mimetype='text/xml')
         data = '<content>here</content>'
 
         res = crud.create_resource(sid, project_id, data, metadata)
@@ -88,17 +90,20 @@ class TestTextgridCrud:
     @staticmethod
     def test_create_revision_and_delete(requests_mock, crud, tgconfig):
         requests_mock.post(
-            tgconfig.host + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&projectId=PROJECT_ID&createRevision=false',
+            tgconfig.host
+            + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&projectId=PROJECT_ID&createRevision=false',
             headers={'Location': 'textgrid:mocked.0'},
             content=binaryFileLoader('crud/kv2q.0.metadata'),
         )
         requests_mock.post(
-            tgconfig.host + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&uri=textgrid%3Akv2q.0&createRevision=true&projectId=PROJECT_ID',
+            tgconfig.host
+            + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&uri=textgrid%3Akv2q.0&createRevision=true&projectId=PROJECT_ID',
             headers={'Location': 'textgrid:mocked.1'},
             content=binaryFileLoader('crud/kv2q.1.metadata'),
         )
         requests_mock.post(
-            tgconfig.host + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&uri=textgrid%3Akv2q.1&createRevision=true&projectId=PROJECT_ID',
+            tgconfig.host
+            + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&uri=textgrid%3Akv2q.1&createRevision=true&projectId=PROJECT_ID',
             headers={'Location': 'textgrid:mocked.2'},
             content=binaryFileLoader('crud/kv2q.2.metadata'),
         )
@@ -117,8 +122,7 @@ class TestTextgridCrud:
         sid = 'SESSION_ID'  # does not matter for mocked requests
         project_id = 'PROJECT_ID'  # does not matter for mocked requests
         now = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
-        metadata = TextgridMetadata().build(
-            title='test '+now, mimetype='text/xml')
+        metadata = TextgridMetadata().build(title='test ' + now, mimetype='text/xml')
         data = '<content>here</content>'
 
         md_rev0 = crud.create_resource(sid, project_id, data, metadata)
@@ -133,8 +137,7 @@ class TestTextgridCrud:
         assert textgrid_rev1_uri.endswith('.1')
         assert md_rev1.object_value.generic.generated.revision == 1
 
-        md_rev2 = crud.update_resource(
-            sid, textgrid_rev1_uri, data, md_rev1, create_revision=True)
+        md_rev2 = crud.update_resource(sid, textgrid_rev1_uri, data, md_rev1, create_revision=True)
         textgrid_rev2_uri = md_rev2.object_value.generic.generated.textgrid_uri.value
         assert textgrid_rev2_uri.startswith('textgrid:')
         assert textgrid_rev2_uri.endswith('.2')
@@ -152,7 +155,8 @@ class TestTextgridCrud:
     @staticmethod
     def test_create_update_metadata_and_delete(requests_mock, crud, tgconfig):
         requests_mock.post(
-            tgconfig.host + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&projectId=PROJECT_ID&createRevision=false',
+            tgconfig.host
+            + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&projectId=PROJECT_ID&createRevision=false',
             headers={'Location': 'textgrid:mocked'},
             content=binaryFileLoader('crud/kv2q.0.metadata'),
         )
@@ -162,18 +166,17 @@ class TestTextgridCrud:
         )
         requests_mock.get(
             tgconfig.host + '/1.0/tgcrud/rest/textgrid:kv2q.0/data?sessionId=SESSION_ID',
-            content=binaryFileLoader('crud/kv2q.0.data')
+            content=binaryFileLoader('crud/kv2q.0.data'),
         )
         requests_mock.post(
             tgconfig.host + '/1.0/tgcrud/rest/textgrid:kv2q.0/updateMetadata?sessionId=SESSION_ID',
-            content=binaryFileLoader('crud/kv2q.0.metadata')
+            content=binaryFileLoader('crud/kv2q.0.metadata'),
         )
         sid = 'SESSION_ID'  # does not matter for mocked requests
         project_id = 'PROJECT_ID'  # does not matter for mocked requests
         now = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
         title = 'Alice im Wunderland'
-        metadata = TextgridMetadata().build(
-            title=title, mimetype='text/xml')
+        metadata = TextgridMetadata().build(title=title, mimetype='text/xml')
         data = '<content>here</content>'
 
         cmeta = crud.create_resource(sid, project_id, data, metadata)
@@ -186,7 +189,8 @@ class TestTextgridCrud:
 
         # we re-use fixtures in mocked response and do not test if the metadata
         # updates (hint: it does not)
-        cmeta2 = crud.update_metadata(sid, textgrid_uri, cmeta)
+        crud.update_metadata(sid, textgrid_uri, cmeta)
+        # cmeta2 = crud.update_metadata(sid, textgrid_uri, cmeta)
         # assert cmeta2.object_value.generic.provided.title[0] == newtitle
 
         res = crud.delete_resource(sid, textgrid_uri)
@@ -195,7 +199,8 @@ class TestTextgridCrud:
     @staticmethod
     def test_create_update_read_and_delete(requests_mock, crud, tgconfig):
         requests_mock.post(
-            tgconfig.host + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&projectId=PROJECT_ID&createRevision=false',
+            tgconfig.host
+            + '/1.0/tgcrud/rest/create?sessionId=SESSION_ID&projectId=PROJECT_ID&createRevision=false',
             headers={'Location': 'textgrid:mocked'},
             content=binaryFileLoader('crud/kv2q.0.metadata'),
         )
@@ -205,21 +210,20 @@ class TestTextgridCrud:
         )
         requests_mock.get(
             tgconfig.host + '/1.0/tgcrud/rest/textgrid:kv2q.0/data?sessionId=SESSION_ID',
-            content=binaryFileLoader('crud/kv2q.0.data')
+            content=binaryFileLoader('crud/kv2q.0.data'),
         )
         requests_mock.get(
             tgconfig.host + '/1.0/tgcrud/rest/textgrid:kv2q.0/metadata',
-            content=binaryFileLoader('crud/kv2q.0.metadata')
+            content=binaryFileLoader('crud/kv2q.0.metadata'),
         )
         requests_mock.post(
             tgconfig.host + '/1.0/tgcrud/rest/textgrid:kv2q.0/update?sessionId=SESSION_ID',
-            content=binaryFileLoader('crud/kv2q.0.metadata')
+            content=binaryFileLoader('crud/kv2q.0.metadata'),
         )
         sid = 'SESSION_ID'  # does not matter for mocked requests
         project_id = 'PROJECT_ID'  # does not matter for mocked requests
         now = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
-        metadata = TextgridMetadata().build(
-            title='test '+now, mimetype='text/plain')
+        metadata = TextgridMetadata().build(title='test ' + now, mimetype='text/plain')
         # the content on creation
         cdata = 'created'
 
@@ -234,8 +238,7 @@ class TestTextgridCrud:
         umetadata = crud.read_metadata(textgrid_uri, sid)
         # updated content
         udata = 'updated'
-        res = crud.update_resource(
-            sid, textgrid_uri, udata, umetadata)
+        res = crud.update_resource(sid, textgrid_uri, udata, umetadata)
         textgrid_uri = res.object_value.generic.generated.textgrid_uri.value
         assert textgrid_uri.startswith('textgrid:')
 
diff --git a/tests/unit/test_textgrid_metadata.py b/tests/unit/test_textgrid_metadata.py
index 05421f6dc51af92c419b7a3474a7c16ff434a20e..50034f450d94809583472d0834c7a3008c80c401 100644
--- a/tests/unit/test_textgrid_metadata.py
+++ b/tests/unit/test_textgrid_metadata.py
@@ -2,49 +2,52 @@
 #
 # SPDX-License-Identifier: LGPL-3.0-or-later
 
-from tgclients.metadata import TextgridMetadata
-from . import fileLoader, FIXTURE_PATH
-
 from tgclients.databinding.tgsearch import Response
+from tgclients.metadata import TextgridMetadata
 
+from . import FIXTURE_PATH
 
 filenames = {
-  'jftx.0' : 'Abraham_a_Sancta_Clara___Kein_Titel_.jftx.0.jpg',
-  'jfsz.0': 'Biographie__Abraham_a_Sancta_Clara.jfsz.0.work',
-  'jfsm.0': 'Abraham_a_Sancta_Clara.jfsm.0.collection',
-  'jfsw.0' : 'Satirischer_Traktat.jfsw.0.aggregation',
-  'jfsx.0' : 'Biographie__Abraham_a_Sancta_Clara.jfsx.0.xml',
-  'noformat.1' : 'Alice_im_Wunderland.noformat.1',
-  'kmwm.0' : 'An_Herren_J.F._Ratschky.kmwm.0.work',
-  'noauth.0' : 'Restricted_TextGrid_Object.noauth.0',
+    'jftx.0': 'Abraham_a_Sancta_Clara___Kein_Titel_.jftx.0.jpg',
+    'jfsz.0': 'Biographie__Abraham_a_Sancta_Clara.jfsz.0.work',
+    'jfsm.0': 'Abraham_a_Sancta_Clara.jfsm.0.collection',
+    'jfsw.0': 'Satirischer_Traktat.jfsw.0.aggregation',
+    'jfsx.0': 'Biographie__Abraham_a_Sancta_Clara.jfsx.0.xml',
+    'noformat.1': 'Alice_im_Wunderland.noformat.1',
+    'kmwm.0': 'An_Herren_J.F._Ratschky.kmwm.0.work',
+    'noauth.0': 'Restricted_TextGrid_Object.noauth.0',
 }
 
 
-def _fixture_2_filename(filename: str)-> str:
-  meta = TextgridMetadata()
-  metastring = open(FIXTURE_PATH + filename, encoding="utf8").read()
-  mdobj: Response = meta.searchresponse2object(metastring)
-  return meta.filename_from_metadata(mdobj.result[0])
+def _fixture_2_filename(filename: str) -> str:
+    meta = TextgridMetadata()
+    metastring = open(FIXTURE_PATH + filename, encoding='utf8').read()
+    mdobj: Response = meta.searchresponse2object(metastring)
+    return meta.filename_from_metadata(mdobj.result[0])
+
 
 def test_filename_from_metadata():
-  for key in filenames:
-    print(_fixture_2_filename(key))
-    assert _fixture_2_filename(key) == filenames[key]
+    for key in filenames:
+        print(_fixture_2_filename(key))
+        assert _fixture_2_filename(key) == filenames[key]
+
 
 def test_extension_from_format():
-  mimetype = 'text/xml'
-  ext = TextgridMetadata().extension_for_format(mimetype)
-  assert ext == 'xml'
+    mimetype = 'text/xml'
+    ext = TextgridMetadata().extension_for_format(mimetype)
+    assert ext == 'xml'
+
+    mimetype = 'image/jpeg'
+    ext = TextgridMetadata().extension_for_format(mimetype)
+    assert ext == 'jpg'
 
-  mimetype = 'image/jpeg'
-  ext = TextgridMetadata().extension_for_format(mimetype)
-  assert ext == 'jpg'
 
 def test_extension_from_unknown_format():
-  mimetype = 'no/this.is.no+known+format'
-  ext = TextgridMetadata().extension_for_format(mimetype)
-  assert ext == None
+    mimetype = 'no/this.is.no+known+format'
+    ext = TextgridMetadata().extension_for_format(mimetype)
+    assert ext is None
+
 
 def test_id_from_filename():
-  for key in filenames:
-    assert TextgridMetadata().id_from_filename(filenames[key]) == key
+    for key in filenames:
+        assert TextgridMetadata().id_from_filename(filenames[key]) == key
diff --git a/tests/unit/test_textgrid_search.py b/tests/unit/test_textgrid_search.py
index 34204a7b960cbecf797ac02f9a4a6e388db37ed7..c62ffa6fdd9b7f1c403c761da4ee2adab3b7205b 100644
--- a/tests/unit/test_textgrid_search.py
+++ b/tests/unit/test_textgrid_search.py
@@ -3,26 +3,26 @@
 # SPDX-License-Identifier: LGPL-3.0-or-later
 
 from tgclients.config import TextgridConfig
-from tgclients.databinding.tgsearch import Response as SearchResponse, TextgridUris
+from tgclients.databinding.tgsearch import Response as SearchResponse
+from tgclients.databinding.tgsearch import TextgridUris
 from tgclients.search import TextgridSearch
 
 from . import fileLoader
 
 
 class TestTextgridSearch:
-
     @staticmethod
     def test_empty_constructor():
         """Not passing a config in constructor should provide a client for
-          tgsearch public on production system"""
+        tgsearch public on production system"""
         tgs = TextgridSearch()
         assert tgs._url == TextgridConfig().search_public
 
     @staticmethod
     def test_public_info(requests_mock, search_public, tgconfig):
         requests_mock.get(
-            tgconfig.host + '/1.0/tgsearch-public/info/textgrid:kv2q.0',
-            text=fileLoader('kv2q.0'))
+            tgconfig.host + '/1.0/tgsearch-public/info/textgrid:kv2q.0', text=fileLoader('kv2q.0')
+        )
 
         uri = 'textgrid:kv2q.0'
         res: SearchResponse = search_public.info(uri)
@@ -38,7 +38,8 @@ class TestTextgridSearch:
     def test_public_aggregation_list(requests_mock, search_public, tgconfig):
         requests_mock.get(
             tgconfig.host + '/1.0/tgsearch-public/navigation/agg/textgrid:kv2p.0',
-            text=fileLoader('agg_kv2p.0'))
+            text=fileLoader('agg_kv2p.0'),
+        )
         uri = 'textgrid:kv2p.0'
         res: SearchResponse = search_public.list_aggregation(uri)
         assert len(res.result) == 2
@@ -47,7 +48,8 @@ class TestTextgridSearch:
     def test_public_search_filter_format(requests_mock, search_public, tgconfig):
         requests_mock.get(
             tgconfig.host + '/1.0/tgsearch-public/search?q=%2A&filter=format%3Atext%2Fxml',
-            text=fileLoader('search/filter_format_xml.xml'))
+            text=fileLoader('search/filter_format_xml.xml'),
+        )
         filters = ['format:text/xml']
         res: SearchResponse = search_public.search(filters=filters)
         assert int(res.hits) > 100
@@ -57,11 +59,12 @@ class TestTextgridSearch:
     @staticmethod
     def test_public_search_filter_format_project(requests_mock, search_public, tgconfig):
         requests_mock.get(
-            tgconfig.host + '/1.0/tgsearch-public/search?q=%2A&filter=format%3Atext%2Fxml&filter=project.id%3ATGPR-372fe6dc-57f2-6cd4-01b5-2c4bbefcfd3c',
-            text=fileLoader('search/filter_format_project.xml'))
+            tgconfig.host
+            + '/1.0/tgsearch-public/search?q=%2A&filter=format%3Atext%2Fxml&filter=project.id%3ATGPR-372fe6dc-57f2-6cd4-01b5-2c4bbefcfd3c',
+            text=fileLoader('search/filter_format_project.xml'),
+        )
         pid = 'TGPR-372fe6dc-57f2-6cd4-01b5-2c4bbefcfd3c'
-        filters = ['format:text/xml',
-                   'project.id:'+pid]
+        filters = ['format:text/xml', 'project.id:' + pid]
         res: SearchResponse = search_public.search(filters=filters)
         assert int(res.hits) > 100
         for r in res.result:
@@ -72,7 +75,8 @@ class TestTextgridSearch:
     def test_edition_work_metadata_for(requests_mock, search_public, tgconfig):
         requests_mock.get(
             tgconfig.host + '/1.0/tgsearch-public/info/textgrid:kv2q.0/editionWorkMeta',
-            text=fileLoader('search/kv2q.0.editionWorkMeta.xml'))
+            text=fileLoader('search/kv2q.0.editionWorkMeta.xml'),
+        )
         uri = 'textgrid:kv2q.0'
         res: SearchResponse = search_public.edition_work_metadata_for(uri)
         print(res)
@@ -86,7 +90,8 @@ class TestTextgridSearch:
     def test_children(requests_mock, search_public, tgconfig):
         requests_mock.get(
             tgconfig.host + '/1.0/tgsearch-public/info/textgrid:kv2t.0/children',
-            text=fileLoader('search/kv2t.0.children.xml'))
+            text=fileLoader('search/kv2t.0.children.xml'),
+        )
         uri = 'textgrid:kv2t.0'
         res: TextgridUris = search_public.children(uri)
         assert 'textgrid:kv2q.0' in res.textgrid_uri
diff --git a/tests/unit/test_textgrid_search_request.py b/tests/unit/test_textgrid_search_request.py
index 3663310b11fd90452bfd5f0f2b537cc049ea1fa6..b407b14a74f809cf163db91c3705500e23344323 100644
--- a/tests/unit/test_textgrid_search_request.py
+++ b/tests/unit/test_textgrid_search_request.py
@@ -9,19 +9,18 @@ from . import fileLoader
 
 
 class TestTextgridSearchRequest:
-
     @staticmethod
     def test_empty_constructor():
         """Not passing a config in constructor should provide a client for
-          tgsearch public on production system"""
+        tgsearch public on production system"""
         tgs = TextgridSearchRequest()
         assert tgs._url == TextgridConfig().search_public
 
     @staticmethod
     def test_public_info(requests_mock, searchrequest_public, tgconfig):
         requests_mock.get(
-            tgconfig.host + '/1.0/tgsearch-public/info/textgrid:kv2q.0',
-            text=fileLoader('kv2q.0'))
+            tgconfig.host + '/1.0/tgsearch-public/info/textgrid:kv2q.0', text=fileLoader('kv2q.0')
+        )
 
         uri = 'textgrid:kv2q.0'
         res = searchrequest_public.info(uri)
@@ -37,7 +36,8 @@ class TestTextgridSearchRequest:
     def test_public_aggregation_list(requests_mock, searchrequest_public, tgconfig):
         requests_mock.get(
             tgconfig.host + '/1.0/tgsearch-public/navigation/agg/textgrid:kv2p.0',
-            text=fileLoader('agg_kv2p.0'))
+            text=fileLoader('agg_kv2p.0'),
+        )
         uri = 'textgrid:kv2p.0'
         res = searchrequest_public.list_aggregation(uri)
         assert res.status_code == 200
diff --git a/tests/unit/test_textgrid_utils.py b/tests/unit/test_textgrid_utils.py
index d3969040a2553c65799ada7c74e0f2dee5ea9884..de2d01ab983e3c5d19c520e8c8dea1a4dc423ab8 100644
--- a/tests/unit/test_textgrid_utils.py
+++ b/tests/unit/test_textgrid_utils.py
@@ -4,6 +4,7 @@
 
 from tgclients.utils import Utils
 
+
 def test_list_to_aggregation_and_back():
     textgrid_uri = 'textgrid:xyz.1'
     members = ['textgrid:1234.0', 'textrid:abcd.0', 'textgrid:4321.0']
@@ -11,6 +12,3 @@ def test_list_to_aggregation_and_back():
     assert agg.startswith('<?xml version="1.0" encoding="UTF-8"?>')
     extracted_list = Utils.aggregation_to_list(agg)
     assert extracted_list == members
-
-
-