Repository: textX/textX (language: python)

Issue:
Support Click 8.0.0
`black`, a popular python formatter, has a lower bound of `click` version `8.0.0`. 
This prevents installation of `textX` and `textX-LS-core` due to incompatibilities of `click`.
Recommend updating `textX` to support `click` versions `8.0.0` and above.
`pkg_resources` is deprecated
I am seeing a deprecation warning while using textx:
```
DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
```
The link states:
> Use of pkg_resources is deprecated in favor of [importlib.resources](https://docs.python.org/3/library/importlib.resources.html#module-importlib.resources), [importlib.metadata](https://docs.python.org/3/library/importlib.metadata.html#module-importlib.metadata) and their backports ([importlib_resources](https://pypi.org/project/importlib_resources), [importlib_metadata](https://pypi.org/project/importlib_metadata)). Some useful APIs are also provided by [packaging](https://pypi.org/project/packaging) (e.g. requirements and version parsing). Users should refrain from new usage of pkg_resources and should work to port to importlib-based solutions.

This should be fixed to avoid being caught out once the package is fully removed.

I noticed this in a CI test for https://github.com/pyccel/pyccel so I have information about multiple OS/Python version combinations if this is helpful.

## System information
OS: Microsoft Windows Server 2022
Python version : 3.10 installed via miniconda
https://github.com/pyccel/pyccel/actions/runs/6495962235/job/17641938056

Did't reproduce on ubuntu-22.04 with Python versions 3.8, 3.9, 3.10, 3.11
Did't reproduce on macos-12 with Python version 3.10
Support Click 8.0.0
`black`, a popular python formatter, has a lower bound of `click` version `8.0.0`. 
This prevents installation of `textX` and `textX-LS-core` due to incompatibilities of `click`.
Recommend updating `textX` to support `click` versions `8.0.0` and above.


Discussion / hints:

@EmilyBourne Thanks for the heads up.
In Python 3.12, it seems this becomes an error:

```python
$ textx generate demo.tx --target dot
Traceback (most recent call last):
...
  File ".../lib/python3.12/site-packages/textx/registration.py", line 6, in <module>
    import pkg_resources
ModuleNotFoundError: No module named 'pkg_resources'
```

From the [Python 3.12 release notes][1]:

> [gh-95299](https://github.com/python/cpython/issues/95299): Do not pre-install setuptools in virtual environments created with [venv](https://docs.python.org/dev/library/venv.html#module-venv). This means that distutils, setuptools, pkg_resources, and easy_install will no longer available by default; to access these run pip install setuptools in the [activated](https://docs.python.org/dev/library/venv.html#venv-explanation) virtual environment.


[1]: https://docs.python.org/dev/whatsnew/3.12.html


Fix patch:
diff --git a/.github/workflows/ci-linux-ubuntu.yml b/.github/workflows/ci-linux-ubuntu.yml
index 2d30ac939..e0f4dc599 100644
--- a/.github/workflows/ci-linux-ubuntu.yml
+++ b/.github/workflows/ci-linux-ubuntu.yml
@@ -12,7 +12,7 @@ jobs:
     runs-on: ubuntu-20.04
     strategy:
       matrix:
-        python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]
+        python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
 
     steps:
     - uses: actions/checkout@v3
@@ -27,15 +27,16 @@ jobs:
       run: |
         cd ..
         git clone --depth=1 --branch=master https://github.com/textX/Arpeggio
-        pip uninstall -y Arpeggio
-        cd Arpeggio && python setup.py install
-        cd ../textX
+        pip install -e Arpeggio --upgrade
+        cd textX
     - name: Run unit tests
       run: |
         ./runtests.sh
-    - name: Coveralls
+    - name: Upload coverage data to coveralls.io
       env:
         COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        COVERALLS_FLAG_NAME: ${{ matrix.python-version }}
+        COVERALLS_PARALLEL: true
       run: |
-        coveralls
+        coveralls --service=github
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 5232d32b7..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-language: python
-python:
-    - "2.7"
-    - "3.4"
-    - "3.5"
-    - "3.6"
-    - "3.7"
-    - "3.8"
-    - "3.9"
-
-before_install:
-    # Installing Arpeggio as a dependency
-    - cd ..
-    - git clone --depth=1 --branch=master git://github.com/textX/Arpeggio.git
-    - cd Arpeggio && python setup.py install
-    - cd ../textX
-
-install:
-    - ./install-test.sh
-
-script:
-    - ./runtests.sh
-
-after_success:
-    - coveralls
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 40a42ef95..321353ed7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,8 +19,26 @@ please take a look at related PRs and issues and see if the change affects you.
 
 - Removed word boundary from INT rule regex. Thanks @NevenaAl for reporting the
   issue and providing the test ([401]).
-
- [#401]: https://github.com/textX/textX/pull/401
+  
+### Changed
+- **(BIC)** Removed Python support for 3.6 and 3.7. The minimal supported version is 3.8.
+- Migrated to pyproject.toml for project configuration.
+- Migrated to importlib for language/generator registration/discovery as
+  setuptools pkg_resources is deprecated and unsupported from Python 3.12. See
+  [411]. Thanks @EmilyBourne and @nchammas for reporting.
+- **(BIC)** Added dependency to `importlib-metadata` backward compatibility
+  package for Python versions < 3.10. If Python is >=3.10 this library is not used.
+- Use [ruff] instead of flake8 for linting. ruff can also be used for code
+  formating.
+- Relaxed dependency for click to accept 8.x. See [414]. Thanks @RootMeanSqr for
+  reporting the issue.
+- Use [flit] for package building and publishing.
+
+[#401]: https://github.com/textX/textX/pull/401
+[#411]: https://github.com/textX/textX/issues/411
+[#414]: https://github.com/textX/textX/issues/414
+[ruff]: https://github.com/astral-sh/ruff
+[flit]: https://flit.pypa.io/
 
 
 ## [3.1.1] (released: 2023-02-10)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c075b75d2..ae0caa75e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -93,42 +93,51 @@ Ready to contribute? Here's how to set up `textX` for local development.
         $ cd textX/
         $ python -m venv venv
         $ source venv/bin/activate
-        $ ./install-dev.sh 
-        
+        $ ./install-dev.sh
+        $ ./install-test.sh
+
     Previous stuff is needed only the first time. To continue working on textX
     later you just do:
-    
+
         $ cd textX/
         $ source venv/bin/activate
-        
+
     Note that on Windows sourcing syntax is a bit different. Check the docs for
     virtualenv.
-        
+
     An excellent overview of available tools for Python environments management
     can be found
     [here](https://stackoverflow.com/questions/41573587/what-is-the-difference-between-venv-pyvenv-pyenv-virtualenv-virtualenvwrappe)
-    
+
     To verify that everything is setup properly run tests:
-    
-        $ flake8
-        $ pytest tests/functional/
+
+        $ ./runtests.sh
 
 4. Create a branch for local development::
 
-        $ git checkout -b name-of-your-bugfix-or-feature
+        $ git checkout -b name-of-your-bugfix-or-feature-branch
 
    Now you can make your changes locally.
 
-5. When you're done making changes, check that your changes pass flake8, the
+   Optionally, run `ruff format` over the files you are changing from time to
+   time to be sure that you adhere to the formatting style best-practices.
+
+        $ ruff format <path to file>
+
+   Please do not reformat the code that you have not changed. If you notice that
+   ruff has reformatted parts of the code that are not part of your change use
+   git to revert those parts.
+
+5. When you're done making changes, check that your changes pass linter, the
    tests, and have a look at the coverage:
-   
-        $ flake8
+
+        $ ruff check textx/ tests/ examples/
         $ pytest tests/functional/
         $ coverage run --source textx -m pytest tests/functional
         $ coverage report
-        
+
    You can run all this at once with provided script `runtests.sh`
-   
+
         $ ./runtests.sh
 
    In case you have doubts, have also a look at the html rendered version of
@@ -140,7 +149,7 @@ Ready to contribute? Here's how to set up `textX` for local development.
 
         $ git add .
         $ git commit -m "Your detailed description of your changes."
-        $ git push origin name-of-your-bugfix-or-feature
+        $ git push origin name-of-your-bugfix-or-feature-branch
 
 7. Submit a pull request through the GitHub website.
 
@@ -150,8 +159,8 @@ Ready to contribute? Here's how to set up `textX` for local development.
 Before you submit a pull request, check that it meets these guidelines:
 
 1. The pull request should include tests.
-2. If the pull request adds/changes functionality, the docs should be updated. 
-3. The pull request should work for Python 3.6+. Check
+2. If the pull request adds/changes functionality, the docs should be updated.
+3. The pull request should work for Python 3.8+. Check
    https://github.com/textX/textX/actions and make sure that the tests pass for
    all supported Python versions.
 
diff --git a/README.md b/README.md
index 7ed3ff164..a9257febd 100644
--- a/README.md
+++ b/README.md
@@ -47,7 +47,7 @@ Point: x=INT ',' y=INT;
 
 # We will provide our class for Point.
 # Classes for other rules will be dynamically generated.
-class Point(object):
+class Point:
     def __init__(self, parent, x, y):
         self.parent = parent
         self.x = x
@@ -193,7 +193,7 @@ MIT
 
 ## Python versions
 
-Tested for 3.6+
+Tested for 3.8+
 
 
 [Arpeggio]: https://github.com/textX/Arpeggio
diff --git a/docs/metamodel.md b/docs/metamodel.md
index a2736b5e6..b0efe6204 100644
--- a/docs/metamodel.md
+++ b/docs/metamodel.md
@@ -20,12 +20,6 @@ Parsing the input and creating the model is done by `model_from_file` and
 When parsing a model file or string a new parser is cloned for each model.
 This parser can be accessed via the model attribute `_tx_parser`.
 
-!!! warning
-
-    textX accepts unicode strings only so `metamodel_from_str` parameter should be
-    `str` for Python 3.x or `unicode` for Python 2.x. That is true for all API
-    parameters where string is accepted. The easiest way for Python 2.x is to use
-    `from __future__ import unicode_literals` at the top of the file.
 
 ## Custom classes
 
@@ -59,7 +53,7 @@ represent.
     ;
     '''
 
-    class Entity(object):
+    class Entity:
       def __init__(self, parent, name, attributes):
         self.parent = parent
         self.name = name
@@ -228,7 +222,7 @@ Let's provide `integer` and `string` Entities to our `Entity` meta-model in
 order to simplify the model creation so that the users can use the names of
 these two entities as the `Attribute` types.
 
-    class Entity(object):
+    class Entity:
         def __init__(self, parent, name, attributes):
             self.parent = parent
             self.name = name
diff --git a/docs/model.md b/docs/model.md
index 9ce115548..7a8fef731 100644
--- a/docs/model.md
+++ b/docs/model.md
@@ -27,13 +27,6 @@ A model is created from the input string using the `model_from_file` and
     The `model_from_file` method takes an optional argument `encoding`
     to control the input encoding of the model file to be loaded.
    
-!!! warning
-
-    textX accepts unicode strings only so `metamodel_from_str` and `model_from_str`
-    parameter should be `str` for Python 3.x or `unicode` for Python 2.x. That is
-    true for all API parameters where string is accepted. The easiest way for Python
-    2.x is to use `from __future__ import unicode_literals` at the top of the file.
-
 
 Let's take the Entity language used in [Custom
 Classes](metamodel.md#custom-classes) section.
diff --git a/docs/registration.md b/docs/registration.md
index f041c5314..40bbef300 100644
--- a/docs/registration.md
+++ b/docs/registration.md
@@ -4,11 +4,12 @@ textX has an API for registration and discovery of languages and code
 generators. This enable developing languages and generators for others to use by
 simply installing from PyPI using `pip`.
 
-textX utilizes `pkg_resources` module found in `setuptools` and its concept of
-*extension point* to declaratively specify the registration of language or
-generator. Each Python project/package may in its `setup.py` declare this
-extensions. Once a Python package which declare the extension is installed in
-the environment, the extension can be dynamically found.
+textX utilizes a concept of [extension
+point](https://packaging.python.org/en/latest/specifications/entry-points/) to
+declaratively specify the registration of language or generator. Each Python
+project/package may in its `pyproject.toml` declare this extensions. Once a
+Python package which declare the extension is installed in the environment, the
+extension can be dynamically found.
 
 To make it easier to find languages and generators on PyPI we recommend the
 following naming scheme for the Python packages that provide a single language
@@ -60,25 +61,12 @@ entity_lang = LanguageDesc('entity',
 ```
 
 The next step is to make this language discoverable by textX. To do this we have
-to register our `entity_lang` object in the `setup.py` entry point named
+to register our `entity_lang` object in the `pyproject.toml` entry point named
 `textx_languages`.
 
-```python
-setup(
-    ...
-    entry_points={
-        'textx_languages': [
-            'entity = entity.metamodel:entity_lang',
-        ],
-    },
-```
-
-Alternatively, you can use `setup.cfg` which is a preferred way:
-
-```conf
-[options.entry_points]
-textx_languages =
-    entity = entity.metamodel:entity_lang
+```toml
+[project.entry-points.textx_languages]
+entity = "entity.metamodel:entity_lang"
 ```
 
 In this example `entity.metamodel` is the Python module where `entity_lang` is defined.
@@ -106,7 +94,7 @@ def entity_lang():
     # E.g. call to metamodel_from_file
 ```
 
-The `setup.py` entry point would be the same.
+The `pyproject.toml` entry point registration would be the same.
 
 
 !!! warning
@@ -200,24 +188,19 @@ entity_java_generator = GeneratorDesc(
 ```
 
 The next step is to make this generator discoverable by textX. To do this we
-have to register our `entity_java_generator` object in the `setup.py` entry
-point named `textx_generators`.
+have to register our `entity_java_generator` object in the `pyproject.toml`
+entry point named `textx_generators`.
 
-```python
-setup(
-    ...
-    entry_points={
-        'textx_generators' : [
-            'entity_java = entity.generators:entity_java_generator,
-        ],
-    },
+```toml
+[project.entry-points.textx_generators]
+entity_java = "entity.generators:entity_java_generator"
 ```
 
 !!! tip
     
     You can also register generator programmatically using [registration API].
     But if you want your generator to be available to `textx` command you should
-    use `setup.py`.
+    use `pyproject.toml`.
     
     
 
@@ -243,7 +226,7 @@ def entity_java_generator(metamodel, model, output_path, overwrite, debug, **cus
     # Some code that perform generation
 ```
 
-The `setup.py` would remain the same.
+The `pyproject.toml` would remain the same.
 
 
 Here is an example of the generator of `dot` files from any textX model. This is
@@ -412,7 +395,8 @@ All classes and functions documented here are directly importable from `textx` m
   instance of `LanguageDesc` as the first parameter or providing separate
   parameters
 - `clear_language_registrations()` - deletes all languages registered
-  programmatically. Note: languages registered through `setup.py` won't be removed
+  programmatically. Note: languages registered through `pyproject.toml` won't be
+  removed
 - `metamodel_for_language(language_name, **kwargs)` - returns a meta-model for
   the given language name. `kwargs` are additional keyword arguments passed to
   meta-model factory callable, similarly to `metamodel_from_str/file`.
@@ -468,8 +452,8 @@ All classes and functions documented here are directly importable from `textx` m
   instance of `GeneratorDesc` as the first parameter or providing separate
   parameters
 - `clear_generator_registrations` - deletes all generators registered
-  programmatically. Note: generators registered through `setup.py` won't be
-  removed
+  programmatically. Note: generators registered through `pyproject.toml` won't
+  be removed
 - `generator` - a decorator used for [generator
   registration](#registering-a-new-generator)
 
diff --git a/docs/textx_command.md b/docs/textx_command.md
index e1097ffa0..868d5b8b6 100644
--- a/docs/textx_command.md
+++ b/docs/textx_command.md
@@ -29,7 +29,8 @@ textX registers several sub-commands:
     textX itself and the commands provided by third-party Python packages.
 
     Please, see [Extending textx command](#extending-textx-command) section
-    bellow on how to define your own sub-commands investigate `setup.py` of textX project. 
+    bellow on how to define your own sub-commands investigate `pyproject.toml` 
+    of the textX project. 
     
     Some of development commands/tools are registered by
     [textX-dev](https://github.com/textX/textX-dev) project which is an optional dev
@@ -98,9 +99,10 @@ Options:
 ## Extending textx command
 
 `textx` command can be extended from other installed Python packages using
-[pkg_resources](https://setuptools.readthedocs.io/en/latest/pkg_resources.html)
-extension points. Using command extension one can add new commands and command
-groups to the `textx` command.
+[entry
+points](https://packaging.python.org/en/latest/specifications/entry-points/).
+Using command extension one can add new commands and command groups to the
+`textx` command.
 
 `textx` uses [click](https://github.com/pallets/click/) library for CLI commands
 processing. That makes really easy to create new commands and command groups.
@@ -127,29 +129,15 @@ def testcommand(textx):
       click.echo("Hello sub-command test!")
 ```
 
-Register new command in your project's `setup.py` file under the entry point
-`textx_commands` (we are assuming that `testcommand` function is in package
-`cli`).
+Register new command in your project's `pyproject.toml` file under the entry
+point `textx_commands` (we are assuming that `testcommand` function is in
+package `cli`).
 
-```python
-setup(
-    name='MyProject',
-    packages=["cli"],
-    entry_points={
-        'textx_commands': [
-            'testcommand = cli:testcommand'
-        ],
-    }
-)
+```toml
+[project.entry-points.textx_commands]
+testcommand = "cli:testcommand"
 ```
 
-!!! tip
-
-    If you prefer a more declarative approach you can use `setup.cfg` instead of
-    `setup.py` to configure your project and register textx commands. For an
-    idea see how [textX project registers its commands](https://github.com/textX/textX/blob/master/setup.cfg).
-
-
 If you install now your project in the same Python environment where `textX` is
 installed you will see that `textx` command now has your command registered.
 
@@ -210,20 +198,11 @@ In this example we created a new group called `testgroup`. We use that group in
 the rest of the code to decorate new commands belonging to the group.
 
 As usual, we have to register our function in the extension point
-`textx_commands`:
+`textx_commands` inside `pyproject.toml`:
 
-```python
-setup(
-    name='MyProject',
-    packages=["cli"],
-    entry_points={
-        ...
-        'textx_commands': [
-            'testgroup = cli:create_testgroup'
-        ]
-    }
-    }
-)
+```toml
+[project.entry-points.textx_commands]
+testgroup = "cli:create_testgroup"
 ```
 
 If `MyProject` is installed in the environment where `textX` is installed you'll
diff --git a/docs/tutorials/entity.md b/docs/tutorials/entity.md
index a0f0de01e..a6fb21412 100644
--- a/docs/tutorials/entity.md
+++ b/docs/tutorials/entity.md
@@ -178,7 +178,7 @@ supplied ones. So, we can create our class `SimpleType` and register that class
 during meta-model instantiation together with two of its instances (`integer` and
 `string`).
 
-    class SimpleType(object):
+    class SimpleType:
       def __init__(self, parent, name):  # remember to include parent param.
         self.parent = parent
         self.name = name
@@ -278,7 +278,7 @@ the entities from our model and generate a Java file for each entity:
     this_folder = dirname(__file__)
 
 
-    class SimpleType(object):
+    class SimpleType:
         def __init__(self, parent, name):
             self.parent = parent
             self.name = name
diff --git a/docs/tutorials/entity/srcgen.py b/docs/tutorials/entity/srcgen.py
index c2f3c83fc..a3d42a0d8 100644
--- a/docs/tutorials/entity/srcgen.py
+++ b/docs/tutorials/entity/srcgen.py
@@ -6,7 +6,7 @@
 this_folder = dirname(__file__)
 
 
-class SimpleType(object):
+class SimpleType:
     def __init__(self, parent, name):
         self.parent = parent
         self.name = name
diff --git a/docs/tutorials/robot.md b/docs/tutorials/robot.md
index b77577ea6..548d6ef08 100644
--- a/docs/tutorials/robot.md
+++ b/docs/tutorials/robot.md
@@ -279,7 +279,7 @@ command.
 Lets imagine that we have a robot that understands our language. In your
 `robot.py` file add:
 
-    class Robot(object):
+    class Robot:
 
         def __init__(self):
             # Initial position is (0,0)
diff --git a/examples/Entity/entity_codegen.py b/examples/Entity/entity_codegen.py
index e0223cf0a..2e0212a00 100644
--- a/examples/Entity/entity_codegen.py
+++ b/examples/Entity/entity_codegen.py
@@ -3,7 +3,8 @@
 template engine (http://jinja.pocoo.org/docs/dev/)
 """
 from os import mkdir
-from os.path import exists, dirname, join
+from os.path import dirname, exists, join
+
 import jinja2
 from entity_test import get_entity_mm
 
diff --git a/examples/StateMachine/smdot.py b/examples/StateMachine/smdot.py
index d137c74e1..fd36e439d 100644
--- a/examples/StateMachine/smdot.py
+++ b/examples/StateMachine/smdot.py
@@ -1,10 +1,10 @@
 """
 Export state machine models to dot.
 """
-from __future__ import unicode_literals
 import codecs
 import sys
 from os.path import dirname, join
+
 from textx import metamodel_from_file
 
 HEADER = '''
@@ -54,11 +54,11 @@ def sm_to_dot(model):
 if __name__ == '__main__':
     this_folder = dirname(__file__)
     if len(sys.argv) != 2:
-        print('Usage: {} <model>\n'.format(sys.argv[0]))
+        print(f'Usage: {sys.argv[0]} <model>\n')
     else:
         model_name = sys.argv[1]
         meta = metamodel_from_file(join(this_folder, 'state_machine.tx'))
         model = meta.model_from_file(model_name)
-        with codecs.open("{}.dot".format(model_name), 'w',
+        with codecs.open(f"{model_name}.dot", 'w',
                          encoding='utf-8') as f:
             f.write(sm_to_dot(model))
diff --git a/examples/StateMachine/state_machine.py b/examples/StateMachine/state_machine.py
index 5f05029fa..f0fc132b0 100644
--- a/examples/StateMachine/state_machine.py
+++ b/examples/StateMachine/state_machine.py
@@ -1,9 +1,10 @@
 import sys
 from os.path import dirname, join
+
 from textx import metamodel_from_file
 
 
-class StateMachine(object):
+class StateMachine:
     """
     StateMachine model interpreter.
     """
@@ -65,7 +66,7 @@ def interpret(self):
 if __name__ == '__main__':
     this_folder = dirname(__file__)
     if len(sys.argv) != 2:
-        print("Usage: python {} <model>".format(sys.argv[0]))
+        print(f"Usage: python {sys.argv[0]} <model>")
     else:
         meta_model = metamodel_from_file(join(this_folder, 'state_machine.tx'))
         model = meta_model.model_from_file(sys.argv[1])
diff --git a/examples/drawing/drawing.py b/examples/drawing/drawing.py
index 8bd315c60..3a8d72b04 100644
--- a/examples/drawing/drawing.py
+++ b/examples/drawing/drawing.py
@@ -1,9 +1,6 @@
-from __future__ import unicode_literals
-
-
 def main(debug=False):
 
-    from textx import metamodel_from_str, get_children_of_type
+    from textx import get_children_of_type, metamodel_from_str
 
     grammar = """
     Model: commands*=DrawCommand;
@@ -19,14 +16,14 @@ def main(debug=False):
 
     # We will provide our class for Point.
     # Classes for other rules will be dynamically generated.
-    class Point(object):
+    class Point:
         def __init__(self, parent, x, y):
             self.parent = parent
             self.x = x
             self.y = y
 
         def __str__(self):
-            return "{},{}".format(self.x, self.y)
+            return f"{self.x},{self.y}"
 
         def __add__(self, other):
             return Point(self.parent, self.x + other.x, self.y + other.y)
@@ -80,7 +77,7 @@ def cname(o):
     # Collect all points starting from the root of the model
     points = get_children_of_type("Point", model)
     for point in points:
-        print('Point: {}'.format(point))
+        print(f'Point: {point}')
 
     # Output:
     # Point: 5,10
diff --git a/examples/expression/boolean.py b/examples/expression/boolean.py
index f0eacd895..09ce4a63c 100644
--- a/examples/expression/boolean.py
+++ b/examples/expression/boolean.py
@@ -1,5 +1,5 @@
-from __future__ import unicode_literals
-from os.path import join, dirname
+from os.path import dirname, join
+
 from textx import metamodel_from_str
 from textx.export import metamodel_export, model_export
 
@@ -16,7 +16,7 @@
 namespace = {}
 
 
-class Bool(object):
+class Bool:
     def __init__(self, **kwargs):
         self.assignments = kwargs.pop('assignments')
         self.expression = kwargs.pop('expression')
@@ -29,7 +29,7 @@ def value(self):
         return self.expression.value
 
 
-class ExpressionElement(object):
+class ExpressionElement:
     def __init__(self, **kwargs):
 
         # textX will pass in parent attribute used for parent-child
@@ -39,7 +39,7 @@ def __init__(self, **kwargs):
         # We have 'op' attribute in all grammar rules
         self.op = kwargs['op']
 
-        super(ExpressionElement, self).__init__()
+        super().__init__()
 
 
 class Or(ExpressionElement):
@@ -63,7 +63,7 @@ def value(self):
 class Not(ExpressionElement):
     def __init__(self, **kwargs):
         self._not = kwargs.pop('_not')
-        super(Not, self).__init__(**kwargs)
+        super().__init__(**kwargs)
 
     @property
     def value(self):
@@ -75,13 +75,13 @@ class Operand(ExpressionElement):
     @property
     def value(self):
         op = self.op
-        if type(op) is bool:
+        if isinstance(op, bool):
             return op
         elif op in namespace:
             return namespace[op]
         else:
-            raise Exception('Unknown variable "{}" at position {}'
-                            .format(op, self._tx_position))
+            raise Exception(f'Unknown variable "{op}" at position {self._tx_position}'
+                            )
 
 
 def main(debug=False):
diff --git a/examples/expression/calc.py b/examples/expression/calc.py
index 71d13b884..dfe46dacc 100644
--- a/examples/expression/calc.py
+++ b/examples/expression/calc.py
@@ -1,5 +1,5 @@
-from __future__ import unicode_literals
-from os.path import join, dirname
+from os.path import dirname, join
+
 from textx import metamodel_from_str
 from textx.export import metamodel_export, model_export
 
@@ -18,7 +18,7 @@
 namespace = {}
 
 
-class Calc(object):
+class Calc:
     def __init__(self, **kwargs):
         self.assignments = kwargs.pop('assignments')
         self.expression = kwargs.pop('expression')
@@ -31,7 +31,7 @@ def value(self):
         return self.expression.value
 
 
-class ExpressionElement(object):
+class ExpressionElement:
     def __init__(self, **kwargs):
 
         # textX will pass in parent attribute used for parent-child
@@ -41,13 +41,13 @@ def __init__(self, **kwargs):
         # We have 'op' attribute in all grammar rules
         self.op = kwargs['op']
 
-        super(ExpressionElement, self).__init__()
+        super().__init__()
 
 
 class Factor(ExpressionElement):
     def __init__(self, **kwargs):
         self.sign = kwargs.pop('sign', '+')
-        super(Factor, self).__init__(**kwargs)
+        super().__init__(**kwargs)
 
     @property
     def value(self):
@@ -90,8 +90,8 @@ def value(self):
         elif op in namespace:
             return namespace[op]
         else:
-            raise Exception('Unknown variable "{}" at position {}'
-                            .format(op, self._tx_position))
+            raise Exception(f'Unknown variable "{op}" at position {self._tx_position}'
+                            )
 
 
 def main(debug=False):
diff --git a/examples/expression/calc_isinstance.py b/examples/expression/calc_isinstance.py
index ee099fa59..b6ab02c02 100644
--- a/examples/expression/calc_isinstance.py
+++ b/examples/expression/calc_isinstance.py
@@ -4,17 +4,10 @@
 types.
 """
 
-from __future__ import unicode_literals
-import sys
-from os.path import join, dirname
-from textx import metamodel_from_str
-from textx import textx_isinstance
-from textx.export import metamodel_export, model_export
+from os.path import dirname, join
 
-if sys.version < '3':
-    text = unicode  # noqa
-else:
-    text = str
+from textx import metamodel_from_str, textx_isinstance
+from textx.export import metamodel_export, model_export
 
 grammar = '''
 Calc: assignments*=Assignment expression=Expression;
@@ -59,8 +52,8 @@ def _is(x, rule):
         return textx_isinstance(x, calc_mm[rule])
 
     def assertIs(x, rule):
-        assert _is(x, rule), 'Unexpected object "{}" to rule "{}"'\
-            .format(x, rule)
+        assert _is(x, rule), f'Unexpected object "{x}" to rule "{rule}"'\
+            
 
     def evaluate(x):
 
@@ -114,8 +107,8 @@ def evaluate(x):
             return evaluate(x.expression)
 
         else:
-            assert False, 'Unexpected object "{}" of type "{}"'\
-                .format(x, type(x))
+            raise AssertionError(f'Unexpected object "{x}" of type "{type(x)}"')
+                
 
     result = evaluate(model)
 
diff --git a/examples/expression/calc_monkey.py b/examples/expression/calc_monkey.py
index 16b50509c..057fc4417 100644
--- a/examples/expression/calc_monkey.py
+++ b/examples/expression/calc_monkey.py
@@ -2,15 +2,8 @@
 This is a variant of calc example using dynamically added properties to
 classes created by textx.
 """
-from __future__ import unicode_literals
-import sys
 from textx import metamodel_from_str
 
-if sys.version < '3':
-    text = unicode  # noqa
-else:
-    text = str
-
 grammar = '''
 Calc: assignments*=Assignment expression=Expression;
 Assignment: variable=ID '=' expression=Expression ';';
diff --git a/examples/expression/calc_processors.py b/examples/expression/calc_processors.py
index 61e777b79..e47f7abe3 100644
--- a/examples/expression/calc_processors.py
+++ b/examples/expression/calc_processors.py
@@ -2,15 +2,8 @@
 This is a variant of calc example using object processors for on-the-fly
 evaluation.
 """
-from __future__ import unicode_literals
-import sys
 from textx import metamodel_from_str
 
-if sys.version < '3':
-    text = unicode  # noqa
-else:
-    text = str
-
 grammar = '''
 Calc: assignments*=Assignment expression=Expression;
 Assignment: variable=ID '=' expression=Expression ';';
diff --git a/examples/hello_world/hello.py b/examples/hello_world/hello.py
index 5cdc9a039..2c9d31e4d 100755
--- a/examples/hello_world/hello.py
+++ b/examples/hello_world/hello.py
@@ -1,5 +1,6 @@
 #!/usr/bin/env python
-from os.path import join, dirname
+from os.path import dirname, join
+
 from textx import metamodel_from_file
 from textx.export import metamodel_export, model_export
 
diff --git a/examples/json/json.py b/examples/json/json.py
index ef1d61b16..e5ba02c62 100644
--- a/examples/json/json.py
+++ b/examples/json/json.py
@@ -1,4 +1,5 @@
-from os.path import join, dirname
+from os.path import dirname, join
+
 from textx import metamodel_from_file
 from textx.export import metamodel_export, model_export
 
diff --git a/examples/pyFlies/experiment.py b/examples/pyFlies/experiment.py
index 44e1ddac8..dec30a505 100644
--- a/examples/pyFlies/experiment.py
+++ b/examples/pyFlies/experiment.py
@@ -1,4 +1,5 @@
-from os.path import join, dirname
+from os.path import dirname, join
+
 from textx import metamodel_from_file
 from textx.export import metamodel_export, model_export
 
diff --git a/examples/render_all_grammars/render_all_grammars.py b/examples/render_all_grammars/render_all_grammars.py
index b650d9a04..4c452ee58 100644
--- a/examples/render_all_grammars/render_all_grammars.py
+++ b/examples/render_all_grammars/render_all_grammars.py
@@ -1,10 +1,10 @@
-from __future__ import unicode_literals
-from textx import metamodel_from_file
-from textx.export import metamodel_export, PlantUmlRenderer
 import fnmatch
 import os
 import sys
-from os.path import sep, join, dirname, exists
+from os.path import dirname, exists, join, sep
+
+from textx import metamodel_from_file
+from textx.export import PlantUmlRenderer, metamodel_export
 
 
 def main(path=None, debug=False, reportfilename=None):
@@ -15,14 +15,14 @@ def main(path=None, debug=False, reportfilename=None):
 
     print("render_all_grammars.py - example program")
     matches = []
-    for root, dirnames, filenames in os.walk(path):
+    for root, _dirnames, filenames in os.walk(path):
         if 'textx_textx' in root:
             # skip semantically invalid grammars
             continue
         for filename in fnmatch.filter(filenames, '*.tx'):
             matches.append((root, filename))
 
-    with open(reportfilename, "wt") as md:
+    with open(reportfilename, "w") as md:
         md.write("# All grammars (*.tx)\n")
         for m in matches:
             inname = join(m[0], m[1])
@@ -45,13 +45,13 @@ def main(path=None, debug=False, reportfilename=None):
             metamodel_export(mm, dest_dot)
             metamodel_export(mm, dest_pu, renderer=PlantUmlRenderer())
 
-            md.write("## {}\n".format(m[1]))
-            md.write(" * source: {}/{}\n".format(m[0], m[1]))
-            md.write(" * basename: {}\n".format(outfname_base))
+            md.write(f"## {m[1]}\n")
+            md.write(f" * source: {m[0]}/{m[1]}\n")
+            md.write(f" * basename: {outfname_base}\n")
             md.write('\n')
-            with open(inname, "rt") as gr:
-                for l in gr:
-                    md.write("\t\t" + l)
+            with open(inname) as gr:
+                for i in gr:
+                    md.write("\t\t" + i)
             md.write('\n')
             rel_dest_dot_png = os.path.relpath(
                 dest_dot_png, dirname(reportfilename))
@@ -67,7 +67,7 @@ def main(path=None, debug=False, reportfilename=None):
     print("how to process and display the output:")
     print("  dot -O -Tpng dot/*.dot")
     print("  plantuml pu/*.pu")
-    print("open the generated {}".format(reportfilename))
+    print(f"open the generated {reportfilename}")
 
 
 if __name__ == "__main__":
diff --git a/examples/robot/robot.py b/examples/robot/robot.py
index f9a84427c..e6097d9e9 100644
--- a/examples/robot/robot.py
+++ b/examples/robot/robot.py
@@ -1,4 +1,5 @@
-from os.path import join, dirname
+from os.path import dirname, join
+
 from textx import metamodel_from_file
 from textx.export import metamodel_export, model_export
 
@@ -14,7 +15,7 @@ def move_command_processor(move_cmd):
         move_cmd.steps = 1
 
 
-class Robot(object):
+class Robot:
 
     def __init__(self):
         # Initial position is (0,0)
diff --git a/examples/workflow/workflow.py b/examples/workflow/workflow.py
index d26da258a..6f608669c 100644
--- a/examples/workflow/workflow.py
+++ b/examples/workflow/workflow.py
@@ -1,4 +1,5 @@
-from os.path import join, dirname
+from os.path import dirname, join
+
 from textx import metamodel_from_file
 from textx.export import metamodel_export, model_export
 
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 000000000..7b8de28b5
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,117 @@
+[project]
+name = "textX"
+version = "3.2.0.dev0"
+description = "Meta-language for DSL implementation inspired by Xtext"
+authors = [
+    {name = "Igor R. Dejanović", email = "igor.dejanovic@gmail.com"},
+    {name = "Pierre Bayerl", email = "noemail@nowhere.com"},
+]
+maintainers = [
+    {name = "Igor R. Dejanović", email = "igor.dejanovic@gmail.com"},
+    {name = "Pierre Bayerl", email = "noemail@nowhere.com"},
+]
+readme = "README.md"
+license = {text = "MIT"}
+keywords = ["parser", "meta-language", "meta-model", "language", "DSL"]
+classifiers = [
+    "Development Status :: 5 - Production/Stable",
+    "Intended Audience :: Developers",
+    "Intended Audience :: Information Technology",
+    "Intended Audience :: Science/Research",
+    "Topic :: Software Development :: Interpreters",
+    "Topic :: Software Development :: Compilers",
+    "Topic :: Software Development :: Libraries :: Python Modules",
+    "License :: OSI Approved :: MIT License",
+    "Operating System :: OS Independent",
+    "Programming Language :: Python :: 3",
+    "Programming Language :: Python :: 3.8",
+    "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
+    "Programming Language :: Python :: 3.12",
+]
+
+requires-python = ">=3.8, <3.13"
+dependencies = [
+    "Arpeggio >= 2.0.0",
+    "importlib-metadata; python_version < '3.10'",  # For entry_points backward compatibility
+]
+
+[project.urls]
+Homepage = "https://textx.github.io/textX/"
+Repository = "https://github.com/textX/textX/"
+Changelog = "https://github.com/textX/textX/blob/master/CHANGELOG.md"
+
+[build-system]
+build-backend = "flit_core.buildapi"
+requires = ["flit_core >=3.8.0,<4"]
+
+[tool.flit.module]
+name = "textx"
+
+[tool.flit.sdist]
+exclude = ["**/.*"]
+
+[project.optional-dependencies]
+cli = [
+    "click >=7.0, <9.0"
+]
+
+dev = [
+    "textX-dev",
+    "click >=7.0, <9.0",
+    "ruff",
+    "flit",
+    "mkdocs",
+    "mike",
+]
+
+test = [
+    "click >=7.0, <9.0",
+    "ruff",
+    "attrs",
+    "jinja2",
+    "coverage",
+    "coveralls",
+    "pytest",
+    "html5lib",
+]
+
+[project.scripts]
+textx = "textx.cli:textx"
+
+[project.entry-points.textx_commands]
+version = "textx.cli.version:version"
+list_languages = "textx.cli.discover:list_languages"
+list_generators = "textx.cli.discover:list_generators"
+generate = "textx.cli.generate:generate"
+check = "textx.cli.check:check"
+
+[project.entry-points.textx_generators]
+textx_dot = "textx.generators:metamodel_generate_dot"
+any_dot = "textx.generators:model_generate_dot"
+textx_plantuml = "textx.generators:metamodel_generate_plantuml"
+
+[project.entry-points.textx_languages]
+textx = "textx.metamodel:textx"
+
+[tool.ruff]
+line-length = 90
+indent-width = 4
+
+[tool.ruff.lint]
+# https://docs.astral.sh/ruff/linter/#rule-selection
+select = [
+    # pycodestyle
+    "E",
+    # Pyflakes
+    "F",
+    # pyupgrade
+    "UP",
+    # flake8-bugbear
+    "B",
+    # flake8-simplify
+    "SIM",
+    # isort
+    "I",
+]
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index bfdac76be..000000000
--- a/setup.cfg
+++ /dev/null
@@ -1,90 +0,0 @@
-[metadata]
-name = textX
-author = Igor R. Dejanovic
-author_email = igor.dejanovic@gmail.com
-license = MIT
-description = Meta-language for DSL implementation inspired by Xtext
-keywords = parser, meta-language, meta-model, language, DSL
-url = https://github.com/textX/textX
-long_description = file: README.md
-long_description_content_type = text/markdown
-classifiers =
-    Development Status :: 5 - Production/Stable
-    Intended Audience :: Developers
-    Intended Audience :: Information Technology
-    Intended Audience :: Science/Research
-    Topic :: Software Development :: Interpreters
-    Topic :: Software Development :: Compilers
-    Topic :: Software Development :: Libraries :: Python Modules
-    License :: OSI Approved :: MIT License
-    Operating System :: OS Independent
-    Programming Language :: Python :: 3
-    Programming Language :: Python :: 3.6
-    Programming Language :: Python :: 3.7
-    Programming Language :: Python :: 3.8
-    Programming Language :: Python :: 3.9
-    Programming Language :: Python :: 3.10
-    Programming Language :: Python :: 3.11
-
-[options]
-packages = textx, textx.scoping, textx.cli
-zip_safe = False
-install_requires =
-    Arpeggio >= 2.0.0
-include_package_data = True
-package_dir =
-    textx = textx
-setup_requires =
-    wheel
-
-[options.extras_require]
-cli =
-    click >=7.0, <8.0
-dev =
-    textX-dev
-    click >=7.0, <8.0
-    setuptools-scm
-    mkdocs
-    mike
-    twine
-test =
-     click >=7.0, <8.0
-     flake8
-     attrs
-     jinja2
-     coverage
-     coveralls
-     pytest
-     html5lib
-
-[options.entry_points]
-console_scripts = 
-    textx = textx.cli:textx
-
-textx_commands = 
-    version = textx.cli.version:version
-    list_languages = textx.cli.discover:list_languages
-    list_generators = textx.cli.discover:list_generators
-    generate = textx.cli.generate:generate
-    check = textx.cli.check:check
-
-textx_generators =
-    textx_dot = textx.generators:metamodel_generate_dot
-    any_dot = textx.generators:model_generate_dot
-    textx_plantuml = textx.generators:metamodel_generate_plantuml
-
-textx_languages =
-    textx = textx.metamodel:textx
-
-[bdist_wheel]
-universal=1
-
-[flake8]
-ignore = E741,W503
-max-line-length = 90
-exclude = .git/*,.eggs/*,
-          textx/six.py,
-          textx/lang.py,
-          tests/perf/*,
-          build/*,site/*,venv*,
-          .ropeproject/*
diff --git a/setup.py b/setup.py
deleted file mode 100644
index b30a37745..000000000
--- a/setup.py
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-import os
-import sys
-from setuptools import setup
-
-this_dir = os.path.abspath(os.path.dirname(__file__))
-
-VERSIONFILE = os.path.join(this_dir, "textx", "__init__.py")
-VERSION = None
-for line in open(VERSIONFILE, "r").readlines():
-    if line.startswith('__version__'):
-        VERSION = line.split('"')[1]
-
-if not VERSION:
-    raise RuntimeError('No version defined in textx.__init__.py')
-
-if sys.argv[-1].startswith('publish'):
-    if os.system("pip list | grep twine"):
-        print("twine not installed.\nUse `pip install twine`.\nExiting.")
-        sys.exit()
-    os.system("python setup.py sdist bdist_wheel")
-    if sys.argv[-1] == 'publishtest':
-        os.system("twine upload -r test dist/*")
-    else:
-        os.system("twine upload dist/*")
-        print("You probably want to also tag the version now:")
-        print("  git tag -a {0} -m 'version {0}'".format(VERSION))
-        print("  git push --tags")
-    sys.exit()
-
-setup(version=VERSION)
diff --git a/textx/__init__.py b/textx/__init__.py
index c552793c3..a961efcc0 100644
--- a/textx/__init__.py
+++ b/textx/__init__.py
@@ -1,22 +1,45 @@
 # flake8: noqa
 from textx.metamodel import metamodel_from_file, metamodel_from_str
-from textx.model import get_children_of_type, get_parent_of_type, \
-    get_model, get_metamodel, get_children, get_location, textx_isinstance, \
-    textxerror_wrap
-from textx.exceptions import TextXError, TextXSyntaxError, \
-    TextXSemanticError, TextXRegistrationError
-from textx.registration import (LanguageDesc, GeneratorDesc,
-                                register_language, register_generator,
-                                language_descriptions, language_description,
-                                generator_descriptions, generator_description,
-                                clear_language_registrations,
-                                clear_generator_registrations,
-                                languages_for_file,
-                                language_for_file,
-                                metamodel_for_language,
-                                metamodel_for_file,
-                                metamodels_for_file,
-                                generator_for_language_target,
-                                generator, language)
+from textx.model import (
+    get_children_of_type,
+    get_parent_of_type,
+    get_model,
+    get_metamodel,
+    get_children,
+    get_location,
+    textx_isinstance,
+    textxerror_wrap,
+)
+from textx.exceptions import (
+    TextXError,
+    TextXSyntaxError,
+    TextXSemanticError,
+    TextXRegistrationError,
+)
+from textx.registration import (
+    LanguageDesc,
+    GeneratorDesc,
+    register_language,
+    register_generator,
+    language_descriptions,
+    language_description,
+    generator_descriptions,
+    generator_description,
+    clear_language_registrations,
+    clear_generator_registrations,
+    languages_for_file,
+    language_for_file,
+    metamodel_for_language,
+    metamodel_for_file,
+    metamodels_for_file,
+    generator_for_language_target,
+    generator,
+    language,
+)
 
-__version__ = "3.2.0.dev"
+try:
+    from importlib.metadata import version
+except ModuleNotFoundError:
+    from importlib_metadata import version
+
+__version__ = version("textx")
diff --git a/textx/cli/__init__.py b/textx/cli/__init__.py
index e89c5ef34..7a52bdead 100644
--- a/textx/cli/__init__.py
+++ b/textx/cli/__init__.py
@@ -1,17 +1,24 @@
 try:
     import click
-except ImportError:
-    raise Exception('textX must be installed with CLI dependencies to use '
-                    'textx command.\npip install textX[cli]')
-import pkg_resources
+except ImportError as e:
+    raise Exception(
+        "textX must be installed with CLI dependencies to use "
+        "textx command.\npip install textX[cli]"
+    ) from e
+
+import sys
+
+if sys.version_info < (3, 10):
+    from importlib_metadata import entry_points
+else:
+    from importlib.metadata import entry_points
 
 
 @click.group()
-@click.option('--debug', default=False, is_flag=True,
-              help="Debug/trace output.")
+@click.option("--debug", default=False, is_flag=True, help="Debug/trace output.")
 @click.pass_context
 def textx(ctx, debug):
-    ctx.obj = {'debug': debug}
+    ctx.obj = {"debug": debug}
 
 
 def register_textx_subcommands():
@@ -23,7 +30,7 @@ def register_textx_subcommands():
     level click `textx` command and register additional commands(s) on it.
     """
     global textx
-    for subcommand in pkg_resources.iter_entry_points(group='textx_commands'):
+    for subcommand in entry_points(group="textx_commands"):
         subcommand.load()(textx)
 
 
diff --git a/textx/cli/check.py b/textx/cli/check.py
index 1d0b78c51..e2737d427 100644
--- a/textx/cli/check.py
+++ b/textx/cli/check.py
@@ -1,30 +1,35 @@
-from __future__ import unicode_literals
 try:
     import click
-except ImportError:
-    raise Exception('textX must be installed with CLI dependencies to use '
-                    'textx command.\npip install textX[cli]')
+except ImportError as e:
+    raise Exception(
+        "textX must be installed with CLI dependencies to use "
+        "textx command.\npip install textX[cli]"
+    ) from e
 import os
-from textx import (metamodel_from_file,
-                   metamodel_for_language,
-                   metamodel_for_file,
-                   TextXError, TextXRegistrationError)
 
+from textx import (
+    TextXError,
+    TextXRegistrationError,
+    metamodel_for_file,
+    metamodel_for_language,
+    metamodel_from_file,
+)
 
-def check(textx):
 
+def check(textx):
     @textx.command()
-    @click.argument('model_files', type=click.Path(), required=True, nargs=-1)
-    @click.option('--language',
-                  help='A name of the language model conforms to.')
-    @click.option('--grammar',
-                  help='A file name of the grammar used as a meta-model.')
-    @click.option('--ignore-case/', '-i/', default=False, is_flag=True,
-                  help='Case-insensitive model parsing. '
-                  'Used only if "grammar" is provided.')
+    @click.argument("model_files", type=click.Path(), required=True, nargs=-1)
+    @click.option("--language", help="A name of the language model conforms to.")
+    @click.option("--grammar", help="A file name of the grammar used as a meta-model.")
+    @click.option(
+        "--ignore-case/",
+        "-i/",
+        default=False,
+        is_flag=True,
+        help="Case-insensitive model parsing. " 'Used only if "grammar" is provided.',
+    )
     @click.pass_context
-    def check(ctx, model_files, language=None, grammar=None,
-              ignore_case=False):
+    def check(ctx, model_files, language=None, grammar=None, ignore_case=False):
         """
         Check/validate model given its file path. If grammar is given use it to
         construct the meta-model. If language is given use it to retrieve the
@@ -54,13 +59,14 @@ def check(ctx, model_files, language=None, grammar=None,
 
         """  # noqa
 
-        debug = ctx.obj['debug']
+        debug = ctx.obj["debug"]
 
         try:
             per_file_metamodel = False
             if grammar:
-                metamodel = metamodel_from_file(grammar, debug=debug,
-                                                ignore_case=ignore_case)
+                metamodel = metamodel_from_file(
+                    grammar, debug=debug, ignore_case=ignore_case
+                )
             elif language:
                 metamodel = metamodel_for_language(language)
             else:
@@ -71,10 +77,10 @@ def check(ctx, model_files, language=None, grammar=None,
                     metamodel = metamodel_for_file(model_file)
 
                 metamodel.model_from_file(model_file, debug=debug)
-                click.echo("{}: OK.".format(os.path.abspath(model_file)))
+                click.echo(f"{os.path.abspath(model_file)}: OK.")
 
         except TextXRegistrationError as e:
-            raise click.ClickException(e.message)
+            raise click.ClickException(e.message) from e
 
         except TextXError as e:
-            raise click.ClickException(str(e))
+            raise click.ClickException(str(e)) from e
diff --git a/textx/cli/discover.py b/textx/cli/discover.py
index 2095cd5ec..5abb0b418 100644
--- a/textx/cli/discover.py
+++ b/textx/cli/discover.py
@@ -1,11 +1,12 @@
 try:
     import click
-except ImportError:
-    raise Exception('textX must be installed with CLI dependencies to use '
-                    'textx command.\npip install textX[cli]')
+except ImportError as e:
+    raise Exception(
+        "textX must be installed with CLI dependencies to use "
+        "textx command.\npip install textX[cli]"
+    ) from e
 
-from textx.registration import (language_descriptions,
-                                generator_descriptions)
+from textx.registration import generator_descriptions, language_descriptions
 
 
 def list_languages(textx):
@@ -15,12 +16,13 @@ def list_languages():
         List all registered languages
         """
         for language in language_descriptions().values():
-            click.echo("{:<30}{:<40}{}"
-                       .format("{} ({})".format(language.name,
-                                                language.pattern),
-                               "{}[{}]".format(language.project_name,
-                                               language.project_version),
-                               language.description))
+            click.echo(
+                "{:<30}{:<40}{}".format(
+                    f"{language.name} ({language.pattern})",
+                    f"{language.project_name}[{language.project_version}]",
+                    language.description,
+                )
+            )
 
 
 def list_generators(textx):
@@ -31,9 +33,10 @@ def list_generators():
         """
         for language in generator_descriptions().values():
             for generator in language.values():
-                click.echo("{:<30}{:<30}{}"
-                           .format("{} -> {}".format(generator.language,
-                                                     generator.target),
-                                   "{}[{}]".format(generator.project_name,
-                                                   generator.project_version),
-                                   generator.description))
+                click.echo(
+                    "{:<30}{:<30}{}".format(
+                        f"{generator.language} -> {generator.target}",
+                        f"{generator.project_name}[{generator.project_version}]",
+                        generator.description,
+                    )
+                )
diff --git a/textx/cli/generate.py b/textx/cli/generate.py
index 1c12b595f..0d96b5b8a 100644
--- a/textx/cli/generate.py
+++ b/textx/cli/generate.py
@@ -1,38 +1,65 @@
 import os
+
 try:
     import click
-except ImportError:
-    raise Exception('textX must be installed with CLI dependencies to use '
-                    'textx command.\npip install textX[cli]')
-from textx import (metamodel_from_file,
-                   metamodel_for_file,
-                   metamodel_for_language,
-                   language_for_file,
-                   generator_for_language_target,
-                   TextXRegistrationError, TextXError)
+except ImportError as e:
+    raise Exception(
+        "textX must be installed with CLI dependencies to use "
+        "textx command.\npip install textX[cli]"
+    ) from e
+from textx import (
+    TextXError,
+    TextXRegistrationError,
+    generator_for_language_target,
+    language_for_file,
+    metamodel_for_file,
+    metamodel_for_language,
+    metamodel_from_file,
+)
 
 
 def generate(textx):
-    @textx.command(
-        context_settings=dict(ignore_unknown_options=True)
+    @textx.command(context_settings=dict(ignore_unknown_options=True))
+    @click.argument("model_files", type=click.Path(), required=True, nargs=-1)
+    @click.option(
+        "--output-path",
+        "-o",
+        type=click.Path(),
+        default=None,
+        help="The output to generate to. Default = same as input.",
+    )
+    @click.option(
+        "--language",
+        help="A name of the language model conforms to."
+        " Deduced from file name if not given.",
+    )
+    @click.option("--target", help="Target output format.", required=True)
+    @click.option(
+        "--overwrite",
+        is_flag=True,
+        default=False,
+        required=False,
+        help="Should overwrite output files if exist.",
+    )
+    @click.option("--grammar", help="A file name of the grammar used as a meta-model.")
+    @click.option(
+        "--ignore-case/",
+        "-i/",
+        default=False,
+        is_flag=True,
+        help="Case-insensitive model parsing. " 'Used only if "grammar" is provided.',
     )
-    @click.argument('model_files', type=click.Path(), required=True, nargs=-1)
-    @click.option('--output-path', '-o', type=click.Path(), default=None,
-                  help='The output to generate to. Default = same as input.')
-    @click.option('--language',
-                  help='A name of the language model conforms to.'
-                  ' Deduced from file name if not given.')
-    @click.option('--target', help='Target output format.', required=True)
-    @click.option('--overwrite', is_flag=True, default=False, required=False,
-                  help='Should overwrite output files if exist.')
-    @click.option('--grammar',
-                  help='A file name of the grammar used as a meta-model.')
-    @click.option('--ignore-case/', '-i/', default=False, is_flag=True,
-                  help='Case-insensitive model parsing. '
-                  'Used only if "grammar" is provided.')
     @click.pass_context
-    def generate(ctx, model_files, output_path, language, target, overwrite,
-                 grammar=None, ignore_case=False):
+    def generate(
+        ctx,
+        model_files,
+        output_path,
+        language,
+        target,
+        overwrite,
+        grammar=None,
+        ignore_case=False,
+    ):
         """
         Run code generator on a provided model(s).
 
@@ -67,15 +94,16 @@ def generate(ctx, model_files, output_path, language, target, overwrite,
 
         """
 
-        debug = ctx.obj['debug']
-        click.echo('Generating {} target from models:'.format(target))
+        debug = ctx.obj["debug"]
+        click.echo(f"Generating {target} target from models:")
 
         try:
             per_file_metamodel = False
             if grammar:
-                metamodel = metamodel_from_file(grammar, debug=debug,
-                                                ignore_case=ignore_case)
-                language = 'any'
+                metamodel = metamodel_from_file(
+                    grammar, debug=debug, ignore_case=ignore_case
+                )
+                language = "any"
             elif language:
                 metamodel = metamodel_for_language(language)
             else:
@@ -87,19 +115,18 @@ def generate(ctx, model_files, output_path, language, target, overwrite,
             custom_args = {}
             while model_files:
                 m = model_files.pop(0)
-                if m.startswith('--'):
+                if m.startswith("--"):
                     arg_name = m[2:]
-                    if not model_files or model_files[0].startswith('--'):
+                    if not model_files or model_files[0].startswith("--"):
                         # Boolean argument
                         custom_args[arg_name] = True
                     else:
-                        custom_args[arg_name] = model_files.pop(0).strip('"\'')
+                        custom_args[arg_name] = model_files.pop(0).strip("\"'")
                 else:
                     model_files_without_args.append(m)
 
             # Call generator for each model file
             for model_file in model_files_without_args:
-
                 click.echo(os.path.abspath(model_file))
 
                 if per_file_metamodel:
@@ -108,18 +135,21 @@ def generate(ctx, model_files, output_path, language, target, overwrite,
 
                 # Get custom args that match defined model parameters and pass
                 # them in to be available to model processors.
-                model_params = {k: v for k, v in custom_args.items()
-                                if k in metamodel.model_param_defs}
+                model_params = {
+                    k: v
+                    for k, v in custom_args.items()
+                    if k in metamodel.model_param_defs
+                }
 
                 model = metamodel.model_from_file(model_file, **model_params)
                 generator = generator_for_language_target(
-                    language, target, any_permitted=per_file_metamodel)
+                    language, target, any_permitted=per_file_metamodel
+                )
 
-                generator(metamodel, model, output_path, overwrite, debug,
-                          **custom_args)
+                generator(metamodel, model, output_path, overwrite, debug, **custom_args)
 
         except TextXRegistrationError as e:
-            raise click.ClickException(e.message)
+            raise click.ClickException(e.message) from e
 
         except TextXError as e:
-            raise click.ClickException(str(e))
+            raise click.ClickException(str(e)) from e
diff --git a/textx/cli/version.py b/textx/cli/version.py
index 8ac419c4b..23f7958f6 100644
--- a/textx/cli/version.py
+++ b/textx/cli/version.py
@@ -1,8 +1,10 @@
 try:
     import click
-except ImportError:
-    raise Exception('textX must be installed with CLI dependencies to use '
-                    'textx command.\npip install textX[cli]')
+except ImportError as e:
+    raise Exception(
+        "textX must be installed with CLI dependencies to use "
+        "textx command.\npip install textX[cli]"
+    ) from e
 
 
 def version(textx):
@@ -12,4 +14,5 @@ def version():
         Print version info.
         """
         import textx
-        click.echo('textX {}'.format(textx.__version__))
+
+        click.echo(f"textX {textx.__version__}")
diff --git a/textx/const.py b/textx/const.py
index 750366bbd..ebe8d29c0 100644
--- a/textx/const.py
+++ b/textx/const.py
@@ -1,8 +1,8 @@
 # Multiplicities
-MULT_ONE = '1'
-MULT_OPTIONAL = '0..1'
-MULT_ZEROORMORE = '0..*'
-MULT_ONEORMORE = '1..*'
+MULT_ONE = "1"
+MULT_OPTIONAL = "0..1"
+MULT_ZEROORMORE = "0..*"
+MULT_ONEORMORE = "1..*"
 
 
 priority = [MULT_OPTIONAL, MULT_ONE, MULT_ZEROORMORE, MULT_ONEORMORE]
diff --git a/textx/exceptions.py b/textx/exceptions.py
index ebbfbba81..bc8899fec 100644
--- a/textx/exceptions.py
+++ b/textx/exceptions.py
@@ -1,7 +1,8 @@
 class TextXError(Exception):
-    def __init__(self, message, line=None, col=None,
-                 err_type=None, filename=None, context=None):
-        super(TextXError, self).__init__(message.encode('utf-8'))
+    def __init__(
+        self, message, line=None, col=None, err_type=None, filename=None, context=None
+    ):
+        super().__init__(message.encode("utf-8"))
         self.line = line
         self.col = col
         self.err_type = err_type
@@ -17,31 +18,44 @@ def __str__(self):
                 str(self.line),
                 str(self.col),
                 self.message,
-                " => '{}'".format(self.context) if self.context else ""
-
+                f" => '{self.context}'" if self.context else "",
             )
         else:
-            return super(TextXError, self).__str__()
+            return super().__str__()
 
 
 class TextXSemanticError(TextXError):
-    def __init__(self, message, line=None, col=None, err_type=None,
-                 expected_obj_cls=None, filename=None, context=None):
-        super(TextXSemanticError, self).__init__(
-            message, line, col, err_type, filename, context)
+    def __init__(
+        self,
+        message,
+        line=None,
+        col=None,
+        err_type=None,
+        expected_obj_cls=None,
+        filename=None,
+        context=None,
+    ):
+        super().__init__(message, line, col, err_type, filename, context)
         # Expected object of class
         self.expected_obj_cls = expected_obj_cls
 
 
 class TextXSyntaxError(TextXError):
-    def __init__(self, message, line=None, col=None, err_type=None,
-                 expected_rules=None, filename=None, context=None):
-        super(TextXSyntaxError, self).__init__(
-            message, line, col, err_type, filename, context)
+    def __init__(
+        self,
+        message,
+        line=None,
+        col=None,
+        err_type=None,
+        expected_rules=None,
+        filename=None,
+        context=None,
+    ):
+        super().__init__(message, line, col, err_type, filename, context)
         # Possible rules on this position
         self.expected_rules = expected_rules
 
 
 class TextXRegistrationError(TextXError):
     def __init__(self, message):
-        super(TextXRegistrationError, self).__init__(message)
+        super().__init__(message)
diff --git a/textx/export.py b/textx/export.py
index 90e778082..9358da0e3 100644
--- a/textx/export.py
+++ b/textx/export.py
@@ -1,22 +1,21 @@
-# -*- coding: utf-8 -*-
 """
 Export of textX based models and metamodels to dot file.
 """
-from __future__ import unicode_literals
-from arpeggio import Match, OrderedChoice, Sequence, OneOrMore, ZeroOrMore, \
-    Optional
-from textx.const import MULT_ZEROORMORE, MULT_ONEORMORE, MULT_ONE, \
-    RULE_ABSTRACT, RULE_COMMON, RULE_MATCH
-from textx.lang import PRIMITIVE_PYTHON_TYPES, BASE_TYPE_NAMES, ALL_TYPE_NAMES
 import codecs
-import sys
-if sys.version < '3':
-    text = unicode  # noqa
-else:
-    text = str
 
+from arpeggio import Match, OneOrMore, Optional, OrderedChoice, Sequence, ZeroOrMore
 
-HEADER = '''
+from textx.const import (
+    MULT_ONE,
+    MULT_ONEORMORE,
+    MULT_ZEROORMORE,
+    RULE_ABSTRACT,
+    RULE_COMMON,
+    RULE_MATCH,
+)
+from textx.lang import ALL_TYPE_NAMES, BASE_TYPE_NAMES, PRIMITIVE_PYTHON_TYPES
+
+HEADER = """
     digraph textX {
     fontname = "Bitstream Vera Sans"
     fontsize = 8
@@ -29,7 +28,7 @@
     edge[dir=black,arrowtail=empty]
 
 
-'''
+"""
 
 
 def dot_match_str(cls, other_match_rules=None):
@@ -37,43 +36,47 @@ def dot_match_str(cls, other_match_rules=None):
     For a given match rule meta-class returns a nice string representation for
     the body.
     """
+
     def r(s):
         # print("==>" + str(s) + " " + s.rule_name)
-        if s.root:
-            # breakpoint()
-            if s in visited or s.rule_name in ALL_TYPE_NAMES or \
-                    (hasattr(s, '_tx_class')
-                     and (s._tx_class._tx_type is not RULE_MATCH
-                     or (s._tx_class in other_match_rules
-                         and s._tx_class is not cls))):
-                # print("==> NAME " + s.rule_name)
-                return s.rule_name
+        if s.root and (
+            s in visited
+            or s.rule_name in ALL_TYPE_NAMES
+            or (
+                hasattr(s, "_tx_class")
+                and (
+                    s._tx_class._tx_type is not RULE_MATCH
+                    or (s._tx_class in other_match_rules and s._tx_class is not cls)
+                )
+            )
+        ):
+            # print("==> NAME " + s.rule_name)
+            return s.rule_name
 
         visited.add(s)
         if isinstance(s, Match):
-            result = text(s)
+            result = str(s)
         elif isinstance(s, OrderedChoice):
             result = "|".join([r(x) for x in s.nodes])
         elif isinstance(s, Sequence):
             result = " ".join([r(x) for x in s.nodes])
         elif isinstance(s, ZeroOrMore):
-            result = "({})*".format(r(s.nodes[0]))
+            result = f"({r(s.nodes[0])})*"
         elif isinstance(s, OneOrMore):
-            result = "({})+".format(r(s.nodes[0]))
+            result = f"({r(s.nodes[0])})+"
         elif isinstance(s, Optional):
-            result = "{}?".format(r(s.nodes[0]))
+            result = f"{r(s.nodes[0])}?"
         else:
             # breakpoint()
             # print("#### {}".format(s.__class__.__name__))
             result = "{}({})".format(
-                s.__class__.__name__,
-                ','.join([r(x) for x in s.nodes]))
+                s.__class__.__name__, ",".join([r(x) for x in s.nodes])
+            )
         return "{}{}".format(result, "-" if s.suppress else "")
 
     mstr = ""
     # print("---------- "+str(cls))
-    if not (cls._tx_type is RULE_ABSTRACT
-            and cls.__name__ != cls._tx_peg_rule.rule_name):
+    if not (cls._tx_type is RULE_ABSTRACT and cls.__name__ != cls._tx_peg_rule.rule_name):
         e = cls._tx_peg_rule
         visited = set()
         if other_match_rules is None:
@@ -81,8 +84,9 @@ def r(s):
         if not isinstance(e, Match):
             visited.add(e)
         if isinstance(e, OrderedChoice):
-            mstr = "|".join([r(x) for x in e.nodes
-                             if x.rule_name in BASE_TYPE_NAMES or not x.root])
+            mstr = "|".join(
+                [r(x) for x in e.nodes if x.rule_name in BASE_TYPE_NAMES or not x.root]
+            )
         elif isinstance(e, Sequence):
             mstr = " ".join([r(x) for x in e.nodes])
         else:
@@ -92,39 +96,37 @@ def r(s):
 
 
 def dot_escape(s):
-    return s.replace('\n', r'\n')\
-            .replace('\\', '\\\\')\
-            .replace('"', r'\"')\
-            .replace('|', r'\|')\
-            .replace('{', r'\{')\
-            .replace('}', r'\}')\
-            .replace('>', r'\>')\
-            .replace('<', r'\<')\
-            .replace('?', r'\?')
-
-
-if sys.version < '3':
-    def html_escape(s):
-        return s.replace("<", "&lt;").replace(">", "&gt;")
-else:
-    def html_escape(s):
-        from html import escape
-        return escape(s)
+    return (
+        s.replace("\n", r"\n")
+        .replace("\\", "\\\\")
+        .replace('"', r"\"")
+        .replace("|", r"\|")
+        .replace("{", r"\{")
+        .replace("}", r"\}")
+        .replace(">", r"\>")
+        .replace("<", r"\<")
+        .replace("?", r"\?")
+    )
+
+
+def html_escape(s):
+    from html import escape
+
+    return escape(s)
 
 
 def dot_repr(o):
-    if type(o) is text:
-        escaped = dot_escape(text(o))
+    if isinstance(o, str):
+        escaped = dot_escape(str(o))
         if len(escaped) > 20:
-            return "'{}...'".format(escaped[:20])
+            return f"'{escaped[:20]}...'"
         else:
-            return "'{}'".format(escaped)
+            return f"'{escaped}'"
     else:
-        return text(o)
-
+        return str(o)
 
-class DotRenderer(object):
 
+class DotRenderer:
     def __init__(self):
         self.match_rules = set()
 
@@ -132,25 +134,26 @@ def get_header(self):
         return HEADER
 
     def get_match_rules_table(self):
-        trailer = ''
+        trailer = ""
         if self.match_rules:
-            trailer = '<table>\n'
+            trailer = "<table>\n"
             for cls in sorted(self.match_rules, key=lambda x: x._tx_fqn):
-                trailer += '\t<tr>\n'
+                trailer += "\t<tr>\n"
                 attrs = dot_match_str(cls, self.match_rules)
-                trailer += '\t\t<td><b>{}</b></td><td>{}</td>\n'.format(
-                    cls.__name__, html_escape(attrs))
-                trailer += '\t</tr>\n'
-            trailer += '</table>'
+                trailer += "\t\t<td><b>{}</b></td><td>{}</td>\n".format(
+                    cls.__name__, html_escape(attrs)
+                )
+                trailer += "\t</tr>\n"
+            trailer += "</table>"
         return trailer
 
     def get_trailer(self):
-        trailer = ''
+        trailer = ""
         if self.match_rules:
-            trailer = 'match_rules [ shape=plaintext, label=< {} >]\n\n'.format(
+            trailer = "match_rules [ shape=plaintext, label=< {} >]\n\n".format(
                 self.get_match_rules_table()
             )
-        return trailer + '\n}\n'
+        return trailer + "\n}\n"
 
     def render_class(self, cls):
         name = cls.__name__
@@ -158,63 +161,62 @@ def render_class(self, cls):
         if cls._tx_type is RULE_MATCH:
             if cls.__name__ not in BASE_TYPE_NAMES:
                 self.match_rules.add(cls)
-            return ''
+            return ""
         elif cls._tx_type is not RULE_ABSTRACT:
             for attr in cls._tx_attrs.values():
                 required = attr.mult in [MULT_ONE, MULT_ONEORMORE]
                 mult_list = attr.mult in [MULT_ZEROORMORE, MULT_ONEORMORE]
-                attr_type = "list[{}]".format(attr.cls.__name__) \
-                    if mult_list else attr.cls.__name__
-                if attr.ref and attr.cls.__name__ != 'OBJECT':
+                attr_type = (
+                    f"list[{attr.cls.__name__}]" if mult_list else attr.cls.__name__
+                )
+                if attr.ref and attr.cls.__name__ != "OBJECT":
                     pass
                 else:
                     # If it is plain type
-                    attrs += '{}: {}\\l'.format(
-                        attr.name, attr_type
-                        if required else r'optional\<{}\>'.format(attr_type))
+                    attrs += "{}: {}\\l".format(
+                        attr.name,
+                        attr_type if required else rf"optional\<{attr_type}\>",
+                    )
         return '{}[ label="{{{}|{}}}"]\n\n'.format(
-            id(cls), "*{}".format(name)
-            if cls._tx_type is RULE_ABSTRACT else name, attrs)
+            id(cls), f"*{name}" if cls._tx_type is RULE_ABSTRACT else name, attrs
+        )
 
     def render_attr_link(self, cls, attr):
-        arrowtail = "arrowtail=diamond, dir=both, " \
-            if attr.cont else ""
-        if attr.ref and attr.cls.__name__ != 'OBJECT':
+        arrowtail = "arrowtail=diamond, dir=both, " if attr.cont else ""
+        if attr.ref and attr.cls.__name__ != "OBJECT":
             # If attribute is a reference
-            mult = attr.mult if not attr.mult == MULT_ONE else ""
-            return '{} -> {}[{}headlabel="{} {}"]\n'\
-                .format(id(cls), id(attr.cls), arrowtail,
-                        attr.name, mult)
+            mult = attr.mult if attr.mult != MULT_ONE else ""
+            return '{} -> {}[{}headlabel="{} {}"]\n'.format(
+                id(cls), id(attr.cls), arrowtail, attr.name, mult
+            )
 
     def render_inherited_by(self, base, special):
-        return '{} -> {} [dir=back]\n'\
-            .format(id(base), id(special))
+        return f"{id(base)} -> {id(special)} [dir=back]\n"
 
 
-class PlantUmlRenderer(object):
-
+class PlantUmlRenderer:
     def __init__(self):
         self.match_rules = set()
 
     def get_header(self):
-        return '''@startuml
+        return """@startuml
 set namespaceSeparator .
-'''
+"""
 
     def get_trailer(self):
-        trailer = ''
+        trailer = ""
         if self.match_rules:
-            trailer += '\nlegend\n'
-            trailer += '  Match rules:\n'
-            trailer += '  |= Name  |= Rule details |\n'
+            trailer += "\nlegend\n"
+            trailer += "  Match rules:\n"
+            trailer += "  |= Name  |= Rule details |\n"
             for cls in self.match_rules:
                 # print("-*-> " + cls.__name__)
-                trailer += '  | {} | {} |\n'.format(
+                trailer += "  | {} | {} |\n".format(
                     cls.__name__,
-                    dot_escape(dot_match_str(cls, self.match_rules))  # reuse
+                    dot_escape(dot_match_str(cls, self.match_rules)),  # reuse
                 )
             trailer += "end legend\n\n"
-        trailer += '@enduml\n'
+        trailer += "@enduml\n"
         return trailer
 
     def render_class(self, cls):
@@ -223,48 +225,43 @@ def render_class(self, cls):
         if cls._tx_type is RULE_MATCH:
             if cls.__name__ not in BASE_TYPE_NAMES:
                 self.match_rules.add(cls)
-            return ''
+            return ""
         elif cls._tx_type is not RULE_COMMON:
             stereotype += cls._tx_type
         else:
             for attr in cls._tx_attrs.values():
                 required = attr.mult in [MULT_ONE, MULT_ONEORMORE]
                 mult_list = attr.mult in [MULT_ZEROORMORE, MULT_ONEORMORE]
-                attr_type = "list[{}]".format(attr.cls.__name__) \
-                    if mult_list else attr.cls.__name__
-                if attr.ref and attr.cls.__name__ != 'OBJECT':
+                attr_type = (
+                    f"list[{attr.cls.__name__}]" if mult_list else attr.cls.__name__
+                )
+                if attr.ref and attr.cls.__name__ != "OBJECT":
                     pass
                 else:
                     if required:
-                        attrs += "  {} : {}\n".format(attr.name, attr_type)
+                        attrs += f"  {attr.name} : {attr_type}\n"
                     else:
-                        attrs += "  {} : optional<{}>\n".format(attr.name,
-                                                                attr_type)
+                        attrs += f"  {attr.name} : optional<{attr_type}>\n"
         if len(stereotype) > 0:
             stereotype = "<<" + stereotype + ">>"
-        return '\n\nclass {} {} {{\n{}}}\n'.format(
-            cls._tx_fqn, stereotype, attrs)
+        return f"\n\nclass {cls._tx_fqn} {stereotype} {{\n{attrs}}}\n"
 
     def render_attr_link(self, cls, attr):
-        if attr.ref and attr.cls.__name__ != 'OBJECT':
+        if attr.ref and attr.cls.__name__ != "OBJECT":
             # If attribute is a reference
             # mult = attr.mult if not attr.mult == MULT_ONE else ""
-            if attr.cont:
-                arr = "*-->"
-            else:
-                arr = "o-->"
+            arr = "*-->" if attr.cont else "o-->"
             name = attr.name
             if attr.mult != "1":
                 name += " " + attr.mult
-            return '{} {} {}: {}\n'.format(
-                cls._tx_fqn, arr, attr.cls._tx_fqn, name)
+            return f"{cls._tx_fqn} {arr} {attr.cls._tx_fqn}: {name}\n"
 
     def render_inherited_by(self, base, special):
-        return '{} <|-- {}\n'.format(base._tx_fqn, special._tx_fqn)
+        return f"{base._tx_fqn} <|-- {special._tx_fqn}\n"
 
 
 def metamodel_export(metamodel, file_name, renderer=None):
-    with codecs.open(file_name, 'w', encoding="utf-8") as f:
+    with codecs.open(file_name, "w", encoding="utf-8") as f:
         metamodel_export_tofile(metamodel, f, renderer)
 
 
@@ -281,13 +278,13 @@ def metamodel_export_tofile(metamodel, f, renderer=None):
             pass
         else:
             for attr in cls._tx_attrs.values():
-                if attr.ref and attr.cls.__name__ != 'OBJECT':
+                if attr.ref and attr.cls.__name__ != "OBJECT":
                     f.write(renderer.render_attr_link(cls, attr))
                 if attr.cls not in classes:
                     f.write(renderer.render_class(attr.cls))
         for inherited_by in cls._tx_inh_by:
             f.write(renderer.render_inherited_by(cls, inherited_by))
-    f.write("{}".format(renderer.get_trailer()))
+    f.write(f"{renderer.get_trailer()}")
 
 
 def model_export(model, file_name, repo=None):
@@ -300,7 +297,7 @@ def model_export(model, file_name, repo=None):
     Returns:
         Nothing
     """
-    with codecs.open(file_name, 'w', encoding="utf-8") as f:
+    with codecs.open(file_name, "w", encoding="utf-8") as f:
         model_export_to_file(f, model, repo)
 
 
@@ -323,9 +320,7 @@ def model_export_to_file(f, model=None, repo=None):
     f.write(HEADER)
 
     def _export(obj):
-
-        if obj is None or id(obj) in processed_set or type(obj) \
-                in PRIMITIVE_PYTHON_TYPES:
+        if obj is None or id(obj) in processed_set or type(obj) in PRIMITIVE_PYTHON_TYPES:
             return
 
         processed_set.add(id(obj))
@@ -333,21 +328,18 @@ def _export(obj):
         attrs = ""
         obj_cls = obj.__class__
         name = ""
-        if hasattr(obj_cls, '_tx_attrs'):
+        if hasattr(obj_cls, "_tx_attrs"):
             for attr_name, attr in obj_cls._tx_attrs.items():
-
                 attr_value = getattr(obj, attr_name)
                 if attr_value is None:
                     continue
 
-                endmark = 'arrowtail=diamond dir=both' if attr.cont else ""
-                required = "+" if attr.mult in \
-                    [MULT_ONE, MULT_ONEORMORE] else ""
+                endmark = "arrowtail=diamond dir=both" if attr.cont else ""
+                required = "+" if attr.mult in [MULT_ONE, MULT_ONEORMORE] else ""
 
                 if attr.mult in [MULT_ONEORMORE, MULT_ZEROORMORE]:
-                    if all([type(x) in PRIMITIVE_PYTHON_TYPES
-                            for x in attr_value]):
-                        attrs += "{}{}:list=[".format(required, attr_name)
+                    if all([type(x) in PRIMITIVE_PYTHON_TYPES for x in attr_value]):
+                        attrs += f"{required}{attr_name}:list=["
                         attrs += ",".join([dot_repr(x) for x in attr_value])
                         attrs += "]\\l"
                     else:
@@ -355,51 +347,62 @@ def _export(obj):
                             if list_obj is not None:
                                 if type(list_obj) in PRIMITIVE_PYTHON_TYPES:
                                     f.write(
-                                        '{} -> "{}:{}" [label="{}:{}" {}]\n'
-                                        .format(id(obj), list_obj,
-                                                type(list_obj).__name__,
-                                                attr_name, idx, endmark))
+                                        '{} -> "{}:{}" [label="{}:{}" {}]\n'.format(
+                                            id(obj),
+                                            list_obj,
+                                            type(list_obj).__name__,
+                                            attr_name,
+                                            idx,
+                                            endmark,
+                                        )
+                                    )
                                 else:
-                                    f.write('{} -> {} [label="{}:{}" {}]\n'
-                                            .format(id(obj), id(list_obj),
-                                                    attr_name, idx, endmark))
+                                    f.write(
+                                        '{} -> {} [label="{}:{}" {}]\n'.format(
+                                            id(obj), id(list_obj), attr_name, idx, endmark
+                                        )
+                                    )
                                     _export(list_obj)
                 else:
-
                     # Plain attributes
-                    if type(attr_value) is text and attr_name != 'name':
+                    if isinstance(attr_value, str) and attr_name != "name":
                         attr_value = dot_repr(attr_value)
 
                     if type(attr_value) in PRIMITIVE_PYTHON_TYPES:
-                        if attr_name == 'name':
+                        if attr_name == "name":
                             name = attr_value
                         else:
                             attrs += "{}{}:{}={}\\l".format(
-                                required, attr_name, type(attr_value)
-                                .__name__, attr_value)
+                                required, attr_name, type(attr_value).__name__, attr_value
+                            )
                     else:
                         # Object references
                         if attr_value is not None:
-                            f.write('{} -> {} [label="{}" {}]\n'.format(
-                                id(obj), id(attr_value),
-                                attr_name, endmark))
+                            f.write(
+                                '{} -> {} [label="{}" {}]\n'.format(
+                                    id(obj), id(attr_value), attr_name, endmark
+                                )
+                            )
                             _export(attr_value)
 
-        name = "{}:{}".format(name, obj_cls.__name__)
+        name = f"{name}:{obj_cls.__name__}"
 
-        f.write('{}[label="{{{}|{}}}"]\n'.format(id(obj), name, attrs))
+        f.write(f'{id(obj)}[label="{{{name}|{attrs}}}"]\n')
 
     def _export_subgraph(m):
         from textx import get_children
-        f.write('subgraph "cluster_{}" {{\n'.format(m._tx_filename))
-        f.write('''
+
+        f.write(f'subgraph "cluster_{m._tx_filename}" {{\n')
+        f.write(
+            f"""
         penwidth=2.0
         color=darkorange4;
-        label = "{}";
-                    '''.format(m._tx_filename))
+        label = "{m._tx_filename}";
+                    """
+        )
         for obj in get_children(lambda _: True, m):
-            f.write('{};\n'.format(id(obj)))
-        f.write('\n}\n')
+            f.write(f"{id(obj)};\n")
+        f.write("\n}\n")
 
     if repo or hasattr(model, "_tx_model_repository"):
         if not repo:
@@ -412,4 +415,4 @@ def _export_subgraph(m):
     else:
         _export(model)
 
-    f.write('\n}\n')
+    f.write("\n}\n")
diff --git a/textx/generators.py b/textx/generators.py
index 7b67a6fdd..8c93eec6b 100644
--- a/textx/generators.py
+++ b/textx/generators.py
@@ -1,7 +1,8 @@
 import os
 from functools import partial
+
+from textx.export import PlantUmlRenderer, metamodel_export, model_export
 from textx.registration import generator
-from textx.export import metamodel_export, model_export, PlantUmlRenderer
 
 
 def get_output_filename(input_file, output_path, fileext):
@@ -16,13 +17,13 @@ def get_output_filename(input_file, output_path, fileext):
 
     base_dir = output_path if output_path else os.path.dirname(input_file)
     base_name, _ = os.path.splitext(os.path.basename(input_file))
-    output_file = os.path.abspath(
-        os.path.join(base_dir, "{}.{}".format(base_name, fileext)))
+    output_file = os.path.abspath(os.path.join(base_dir, f"{base_name}.{fileext}"))
     return output_file
 
 
-def gen_file(input_file, output_file, gen_callback, overwrite=False,
-             success_message='Done.'):
+def gen_file(
+    input_file, output_file, gen_callback, overwrite=False, success_message="Done."
+):
     """
     A helper function to implement common logic for generating of a single
     file. Handling of output name creation, skipping existing files, handling
@@ -38,51 +39,63 @@ def gen_file(input_file, output_file, gen_callback, overwrite=False,
     """
     try:
         import click
-    except ImportError:
-        raise Exception('textX must be installed with CLI dependencies to use '
-                        'textx command.\npip install textX[cli]')
+    except ImportError as e:
+        raise Exception(
+            "textX must be installed with CLI dependencies to use "
+            "textx command.\npip install textX[cli]"
+        ) from e
     if overwrite or not os.path.exists(output_file):
-        click.echo('-> {}'.format(output_file))
+        click.echo(f"-> {output_file}")
         gen_callback()
-        click.echo('    ' + success_message)
+        click.echo("    " + success_message)
     else:
-        click.echo(click.style('-- NOT overwriting: ', fg='red', bold=True), nl=False)
+        click.echo(click.style("-- NOT overwriting: ", fg="red", bold=True), nl=False)
         click.echo(output_file)
 
 
-@generator('textX', 'dot')
+@generator("textX", "dot")
 def metamodel_generate_dot(metamodel, model, output_path, overwrite, debug):
     "Generating dot visualizations from textX grammars"
 
-    output_file = get_output_filename(model.file_name, output_path, 'dot')
-    gen_file(model.file_name, output_file,
-             partial(metamodel_export, model, output_file),
-             overwrite,
-             success_message='To convert to png run "dot -Tpng -O {}"'
-             .format(os.path.basename(output_file)))
+    output_file = get_output_filename(model.file_name, output_path, "dot")
+    gen_file(
+        model.file_name,
+        output_file,
+        partial(metamodel_export, model, output_file),
+        overwrite,
+        success_message='To convert to png run "dot -Tpng -O {}"'.format(
+            os.path.basename(output_file)
+        ),
+    )
 
 
-@generator('any', 'dot')
+@generator("any", "dot")
 def model_generate_dot(metamodel, model, output_path, overwrite, debug):
     "Generating dot visualizations from arbitrary models"
 
-    output_file = get_output_filename(model._tx_filename, output_path, 'dot')
-    gen_file(model._tx_filename, output_file,
-             partial(model_export, model, output_file),
-             overwrite,
-             success_message='To convert to png run "dot -Tpng -O {}"'
-             .format(os.path.basename(output_file)))
+    output_file = get_output_filename(model._tx_filename, output_path, "dot")
+    gen_file(
+        model._tx_filename,
+        output_file,
+        partial(model_export, model, output_file),
+        overwrite,
+        success_message='To convert to png run "dot -Tpng -O {}"'.format(
+            os.path.basename(output_file)
+        ),
+    )
 
 
-@generator('textX', 'PlantUML')
-def metamodel_generate_plantuml(metamodel, model, output_path, overwrite,
-                                debug):
+@generator("textX", "PlantUML")
+def metamodel_generate_plantuml(metamodel, model, output_path, overwrite, debug):
     "Generating PlantUML visualizations from textX grammars"
 
-    output_file = get_output_filename(model.file_name, output_path, 'pu')
-    gen_file(model.file_name, output_file,
-             partial(metamodel_export, model, output_file,
-                     renderer=PlantUmlRenderer()),
-             overwrite,
-             success_message='To convert to png run "plantuml {}"'
-             .format(os.path.basename(output_file)))
+    output_file = get_output_filename(model.file_name, output_path, "pu")
+    gen_file(
+        model.file_name,
+        output_file,
+        partial(metamodel_export, model, output_file, renderer=PlantUmlRenderer()),
+        overwrite,
+        success_message='To convert to png run "plantuml {}"'.format(
+            os.path.basename(output_file)
+        ),
+    )
diff --git a/textx/lang.py b/textx/lang.py
index 7a984dfde..d5012ef38 100644
--- a/textx/lang.py
+++ b/textx/lang.py
@@ -6,128 +6,269 @@
 have named this language textX ;)
 
 """
-from __future__ import unicode_literals
-import re
 import codecs
-from arpeggio import StrMatch, Optional, ZeroOrMore, OneOrMore, Sequence,\
-    OrderedChoice, UnorderedGroup, Not, And, RegExMatch, Match, NoMatch, EOF, \
-    ParsingExpression, ParserPython, visit_parse_tree
-from arpeggio.export import PMDOTExporter
+import re
+
+from arpeggio import (
+    EOF,
+    And,
+    Match,
+    NoMatch,
+    Not,
+    OneOrMore,
+    Optional,
+    OrderedChoice,
+    ParserPython,
+    ParsingExpression,
+    RegExMatch,
+    Sequence,
+    StrMatch,
+    UnorderedGroup,
+    ZeroOrMore,
+    visit_parse_tree,
+)
 from arpeggio import RegExMatch as _
-from textx.scoping.rrel import rrel_expression, RRELVisitor
+from arpeggio.export import PMDOTExporter
 
-from .exceptions import TextXError, TextXSyntaxError, TextXSemanticError
-from .const import MULT_ONE, MULT_ZEROORMORE, MULT_ONEORMORE, \
-    MULT_OPTIONAL, RULE_COMMON, RULE_MATCH, RULE_ABSTRACT, mult_lt
+from textx.scoping.rrel import RRELVisitor, rrel_expression
 
-import sys
-if sys.version < '3':
-    text = unicode  # noqa
-else:
-    text = str
+from .const import (
+    MULT_ONE,
+    MULT_ONEORMORE,
+    MULT_OPTIONAL,
+    MULT_ZEROORMORE,
+    RULE_ABSTRACT,
+    RULE_COMMON,
+    RULE_MATCH,
+    mult_lt,
+)
+from .exceptions import TextXError, TextXSemanticError, TextXSyntaxError
 
 # Interpreting backslash sequences.
 # See https://stackoverflow.com/a/24519338/2024430
-ESCAPE_SEQUENCE_RE = re.compile(r'''
+ESCAPE_SEQUENCE_RE = re.compile(
+    r"""
     ( \\U........      # 8-digit hex escapes
     | \\u....          # 4-digit hex escapes
     | \\x..            # 2-digit hex escapes
     | \\[0-7]{1,3}     # Octal escapes
     | \\N\{[^}]+\}     # Unicode characters by name
     | \\[\\'"abfnrtv]  # Single-character escapes
-    )''', re.UNICODE | re.VERBOSE)
+    )""",
+    re.UNICODE | re.VERBOSE,
+)
+
 
 def decode_escapes(s):
     def decode_match(match):
-        return codecs.decode(match.group(0), 'unicode-escape')
+        return codecs.decode(match.group(0), "unicode-escape")
 
     return ESCAPE_SEQUENCE_RE.sub(decode_match, s)
 
+
 # textX grammar
-def textx_model():          return (ZeroOrMore(import_or_reference_stm),
-                                    ZeroOrMore(textx_rule), EOF)
-def import_or_reference_stm(): return [import_stm, reference_stm]
-def import_stm():           return 'import', grammar_to_import
-def reference_stm():        return ('reference', language_name,
-                                    Optional(language_alias))
-def language_alias():       return 'as', ident
-def language_name():        return _(r'(\w|-)+')
-def grammar_to_import():    return _(r'(\w|\.)+')
+def textx_model():
+    return (ZeroOrMore(import_or_reference_stm), ZeroOrMore(textx_rule), EOF)
+
+
+def import_or_reference_stm():
+    return [import_stm, reference_stm]
+
+
+def import_stm():
+    return "import", grammar_to_import
+
+
+def reference_stm():
+    return ("reference", language_name, Optional(language_alias))
+
+
+def language_alias():
+    return "as", ident
+
+
+def language_name():
+    return _(r"(\w|-)+")
+
+
+def grammar_to_import():
+    return _(r"(\w|\.)+")
+
 
 # Rules
-def textx_rule():           return rule_name, Optional(rule_params), ":", textx_rule_body, ";"
-def rule_params():          return '[', rule_param, ZeroOrMore(',', rule_param), ']'
-def rule_param():           return param_name, Optional('=', string_value)
-def param_name():           return ident
-def textx_rule_body():      return choice
-
-def choice():               return sequence, ZeroOrMore("|", sequence)
-def sequence():             return OneOrMore(repeatable_expr)
-def repeatable_expr():      return expression, Optional(repeat_operator), Optional('-')
-def expression():           return [assignment, (Optional(syntactic_predicate),
-                                                 [simple_match, rule_ref,
-                                                  bracketed_choice])]
-def bracketed_choice():     return '(', choice, ')'
-def repeat_operator():      return ['*', '?', '+', '#'], Optional(repeat_modifiers)
-def repeat_modifiers():     return '[', OneOrMore([simple_match,
-                                                   'eolterm']), ']'
-def syntactic_predicate():  return ['!', '&']
-def simple_match():         return [str_match, re_match]
+def textx_rule():
+    return rule_name, Optional(rule_params), ":", textx_rule_body, ";"
+
+
+def rule_params():
+    return "[", rule_param, ZeroOrMore(",", rule_param), "]"
+
+
+def rule_param():
+    return param_name, Optional("=", string_value)
+
+
+def param_name():
+    return ident
+
+
+def textx_rule_body():
+    return choice
+
+
+def choice():
+    return sequence, ZeroOrMore("|", sequence)
+
+
+def sequence():
+    return OneOrMore(repeatable_expr)
+
+
+def repeatable_expr():
+    return expression, Optional(repeat_operator), Optional("-")
+
+
+def expression():
+    return [
+        assignment,
+        (Optional(syntactic_predicate), [simple_match, rule_ref, bracketed_choice]),
+    ]
+
+
+def bracketed_choice():
+    return "(", choice, ")"
+
+
+def repeat_operator():
+    return ["*", "?", "+", "#"], Optional(repeat_modifiers)
+
+
+def repeat_modifiers():
+    return "[", OneOrMore([simple_match, "eolterm"]), "]"
+
+
+def syntactic_predicate():
+    return ["!", "&"]
+
+
+def simple_match():
+    return [str_match, re_match]
+
 
 # Assignment
-def assignment():           return attribute, assignment_op, assignment_rhs
-def attribute():            return ident
-def assignment_op():        return ["=", "*=", "+=", "?="]
-def assignment_rhs():       return [simple_match, reference], Optional(repeat_modifiers)
+def assignment():
+    return attribute, assignment_op, assignment_rhs
+
+
+def attribute():
+    return ident
+
+
+def assignment_op():
+    return ["=", "*=", "+=", "?="]
+
+
+def assignment_rhs():
+    return [simple_match, reference], Optional(repeat_modifiers)
+
 
 # References
-def reference():            return [rule_ref, obj_ref]
-def rule_ref():             return ident
+def reference():
+    return [rule_ref, obj_ref]
+
+
+def rule_ref():
+    return ident
+
+
 # TODO: Remove "|" optional sep in version 4.0.
-def obj_ref():              return '[', class_name, Optional([':', '|'], obj_ref_rule, Optional('|', rrel_expression)), ']'
+def obj_ref():
+    return (
+        "[",
+        class_name,
+        Optional([":", "|"], obj_ref_rule, Optional("|", rrel_expression)),
+        "]",
+    )
+
+
+def rule_name():
+    return ident
+
+
+def obj_ref_rule():
+    return ident
+
+
+def class_name():
+    return qualified_ident
 
-def rule_name():            return ident
-def obj_ref_rule():         return ident
-def class_name():           return qualified_ident
 
-def str_match():            return string_value
-def re_match():             return _(r"/((?:(?:\\/)|[^/])*)/")
-def ident():                return _(r'\w+')
-def qualified_ident():      return _(r'\w+(\.\w+)?')
-def integer():              return _(r'[-+]?[0-9]+')
-def string_value():         return [_(r"'((\\')|[^'])*'"),
-                                    _(r'"((\\")|[^"])*"')]
+def str_match():
+    return string_value
+
+
+def re_match():
+    return _(r"/((?:(?:\\/)|[^/])*)/")
+
+
+def ident():
+    return _(r"\w+")
+
+
+def qualified_ident():
+    return _(r"\w+(\.\w+)?")
+
+
+def integer():
+    return _(r"[-+]?[0-9]+")
+
+
+def string_value():
+    return [_(r"'((\\')|[^'])*'"), _(r'"((\\")|[^"])*"')]
+
 
 # Comments
-def comment():              return [comment_line, comment_block]
-def comment_line():         return _(r'//.*?$')
-def comment_block():        return _(r'/\*(.|\n)*?\*/')
+def comment():
+    return [comment_line, comment_block]
+
+
+def comment_line():
+    return _(r"//.*?$")
 
 
+def comment_block():
+    return _(r"/\*(.|\n)*?\*/")
+
 
 # Special rules - primitive types
-ID          = _(r'[^\d\W]\w*\b', rule_name='ID', root=True)
-BOOL        = _(r'(True|true|False|false|0|1)\b', rule_name='BOOL', root=True)
-INT         = _(r'[-+]?[0-9]+', rule_name='INT', root=True)
-FLOAT       = _(r'[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?(?<=[\w\.])(?![\w\.])',
-                'FLOAT', root=True)
-STRICTFLOAT = _(r'[+-]?(((\d+\.(\d*)?|\.\d+)([eE][+-]?\d+)?)|((\d+)([eE][+-]?\d+)))(?<=[\w\.])(?![\w\.])',
-                'STRICTFLOAT', root=True)
-STRING      = _(r'("(\\"|[^"])*")|(\'(\\\'|[^\'])*\')', 'STRING', root=True)
-NUMBER      = OrderedChoice(nodes=[STRICTFLOAT, INT], rule_name='NUMBER', root=True)
-BASETYPE    = OrderedChoice(nodes=[NUMBER, FLOAT, BOOL, ID, STRING],
-                            rule_name='BASETYPE', root=True)
+ID = _(r"[^\d\W]\w*\b", rule_name="ID", root=True)
+BOOL = _(r"(True|true|False|false|0|1)\b", rule_name="BOOL", root=True)
+INT = _(r"[-+]?[0-9]+", rule_name="INT", root=True)
+FLOAT = _(
+    r"[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?(?<=[\w\.])(?![\w\.])", "FLOAT", root=True
+)
+STRICTFLOAT = _(
+    r"[+-]?(((\d+\.(\d*)?|\.\d+)([eE][+-]?\d+)?)|((\d+)([eE][+-]?\d+)))(?<=[\w\.])(?![\w\.])",
+    "STRICTFLOAT",
+    root=True,
+)
+STRING = _(r'("(\\"|[^"])*")|(\'(\\\'|[^\'])*\')', "STRING", root=True)
+NUMBER = OrderedChoice(nodes=[STRICTFLOAT, INT], rule_name="NUMBER", root=True)
+BASETYPE = OrderedChoice(
+    nodes=[NUMBER, FLOAT, BOOL, ID, STRING], rule_name="BASETYPE", root=True
+)
 
 # A dummy rule for generic type. This rule should never be used for parsing.
-OBJECT = _(r'', rule_name='OBJECT', root=True)
+OBJECT = _(r"", rule_name="OBJECT", root=True)
 
-BASE_TYPE_RULES = {rule.rule_name: rule
-                   for rule in [ID, BOOL, INT, FLOAT, STRICTFLOAT,
-                                STRING, NUMBER, BASETYPE]}
+BASE_TYPE_RULES = {
+    rule.rule_name: rule
+    for rule in [ID, BOOL, INT, FLOAT, STRICTFLOAT, STRING, NUMBER, BASETYPE]
+}
 BASE_TYPE_NAMES = list(BASE_TYPE_RULES.keys())
-ALL_TYPE_NAMES = BASE_TYPE_NAMES + ['OBJECT']
+ALL_TYPE_NAMES = BASE_TYPE_NAMES + ["OBJECT"]
 
-PRIMITIVE_PYTHON_TYPES = [int, float, text, bool]
+PRIMITIVE_PYTHON_TYPES = [int, float, str, bool]
 
 for regex in [ID, BOOL, INT, FLOAT, STRICTFLOAT, STRING]:
     regex.compile()
@@ -136,18 +277,18 @@ def comment_block():        return _(r'/\*(.|\n)*?\*/')
 def python_type(textx_type_name):
     """Return Python type from the name of base textx type."""
     return {
-        'ID': text,
-        'BOOL': bool,
-        'INT': int,
-        'FLOAT': float,
-        'STRICTFLOAT': float,
-        'STRING': text,
-        'NUMBER': float,
-        'BASETYPE': text,
+        "ID": str,
+        "BOOL": bool,
+        "INT": int,
+        "FLOAT": float,
+        "STRICTFLOAT": float,
+        "STRING": str,
+        "NUMBER": float,
+        "BASETYPE": str,
     }.get(textx_type_name, textx_type_name)
 
 
-class RuleCrossRef(object):
+class RuleCrossRef:
     """
     Used during meta-model parser construction for cross reference resolving
     of PEG rules, to support forward references.
@@ -163,6 +304,7 @@ class RuleCrossRef(object):
         position(int): A position in the input string of this cross-ref.
         rrel_tree: the RREL tree defined for this reference
     """
+
     def __init__(self, rule_name, cls, position, rrel_tree):
         self.rule_name = rule_name
         self.cls = cls
@@ -171,16 +313,14 @@ def __init__(self, rule_name, cls, position, rrel_tree):
         self.scope_provider = None
         if rrel_tree is not None:
             from textx.scoping.rrel import create_rrel_scope_provider
+
             self.scope_provider = create_rrel_scope_provider(rrel_tree)
 
     def __str__(self):
         return self.rule_name
 
-    def __unicode__(self):
-        return self.__str__()
-
 
-class ClassCrossRef(object):
+class ClassCrossRef:
     """
     Used for class reference resolving on the meta-model level.
     References will be resolved after semantic analysis of the meta-model
@@ -190,13 +330,13 @@ class ClassCrossRef(object):
         cls_name(str): A name of the target meta-model class.
         position(int): The position in the input string of this cross-ref.
     """
+
     def __init__(self, cls_name, position=0):
         self.cls_name = cls_name
         self.position = position
 
 
 class TextXVisitor(RRELVisitor):
-
     def __init__(self, grammar_parser, metamodel):
         self.grammar_parser = grammar_parser
         self.metamodel = metamodel
@@ -207,27 +347,30 @@ def __init__(self, grammar_parser, metamodel):
         flags = 0
         if metamodel.ignore_case:
             flags = re.IGNORECASE
-        self.keyword_regex = re.compile(r'[^\d\W]\w*', flags)
+        self.keyword_regex = re.compile(r"[^\d\W]\w*", flags)
 
-        super(TextXVisitor, self).__init__()
+        super().__init__()
 
     def visit_textx_model(self, node, children):
-
-        if 'Comment' in self.metamodel:
-            comments_model = self.metamodel['Comment']._tx_peg_rule
+        if "Comment" in self.metamodel:
+            comments_model = self.metamodel["Comment"]._tx_peg_rule
         else:
             comments_model = None
 
         root_rule = children[0]
         from .model import get_model_parser
-        model_parser = get_model_parser(root_rule, comments_model,
-                                        ignore_case=self.metamodel.ignore_case,
-                                        skipws=self.metamodel.skipws,
-                                        ws=self.metamodel.ws,
-                                        autokwd=self.metamodel.autokwd,
-                                        memoization=self.metamodel.memoization,
-                                        debug=self.metamodel.debug,
-                                        file=self.metamodel.file)
+
+        model_parser = get_model_parser(
+            root_rule,
+            comments_model,
+            ignore_case=self.metamodel.ignore_case,
+            skipws=self.metamodel.skipws,
+            ws=self.metamodel.ws,
+            autokwd=self.metamodel.autokwd,
+            memoization=self.metamodel.memoization,
+            debug=self.metamodel.debug,
+            file=self.metamodel.file,
+        )
 
         model_parser.metamodel = self.metamodel
 
@@ -260,33 +403,33 @@ def _resolve_rule(rule):
             resolved_rules.add(rule)
 
             if grammar_parser.debug:
-                grammar_parser.dprint("Resolving rule: {}".format(rule))
+                grammar_parser.dprint(f"Resolving rule: {rule}")
 
-            if type(rule) is RuleCrossRef:
+            if isinstance(rule, RuleCrossRef):
                 rule_name = rule.rule_name
                 suppress = rule.suppress
                 if rule_name in model_parser.metamodel:
                     rule = model_parser.metamodel[rule_name]._tx_peg_rule
-                    if type(rule) is RuleCrossRef:
+                    if isinstance(rule, RuleCrossRef):
                         rule = _resolve_rule(rule)
                         model_parser.metamodel[rule_name]._tx_peg_rule = rule
                     if suppress:
                         # Special case. Suppression on rule reference.
                         _tx_class = rule._tx_class
-                        rule = Sequence(nodes=[rule],
-                                        rule_name=rule_name,
-                                        suppress=suppress)
+                        rule = Sequence(
+                            nodes=[rule], rule_name=rule_name, suppress=suppress
+                        )
                         rule._tx_class = _tx_class
                 else:
                     line, col = grammar_parser.pos_to_linecol(rule.position)
                     raise TextXSemanticError(
-                        'Unexisting rule "{}" at position {}.'
-                        .format(rule.rule_name,
-                                (line, col)), line, col,
-                        filename=model_parser.metamodel.file_name)
+                        f'Unexisting rule "{rule.rule_name}" at position {(line, col)}.',
+                        line,
+                        col,
+                        filename=model_parser.metamodel.file_name,
+                    )
 
-            assert isinstance(rule, ParsingExpression),\
-                "{}:{}".format(type(rule), text(rule))
+            assert isinstance(rule, ParsingExpression), f"{type(rule)}:{str(rule)}"
 
             # Recurse into subrules, and resolve rules.
             for idx, child in enumerate(rule.nodes):
@@ -299,8 +442,7 @@ def _resolve_rule(rule):
         # Two pass resolving
         for i in range(2):
             if grammar_parser.debug:
-                grammar_parser.dprint("RESOLVING RULE CROSS-REFS - PASS {}"
-                                      .format(i + 1))
+                grammar_parser.dprint(f"RESOLVING RULE CROSS-REFS - PASS {i + 1}")
 
             resolved_rules = set()
             _resolve_rule(model_parser.parser_model)
@@ -355,6 +497,7 @@ def _has_nonmatch_ref(rule):
                             result = _has_nonmatch_ref(r)
                         if result:
                             return True
+
                 abstract = _has_nonmatch_ref(rule)
 
             if abstract and cls._tx_type != RULE_ABSTRACT:
@@ -369,13 +512,15 @@ def _has_nonmatch_ref(rule):
                     def _add_reffered_classes(rule, inh_by, start=False):
                         if rule.root and not start:
                             _determine_rule_type(rule._tx_class)
-                            if rule._tx_class._tx_type != RULE_MATCH and\
-                                    rule._tx_class not in inh_by:
+                            if (
+                                rule._tx_class._tx_type != RULE_MATCH
+                                and rule._tx_class not in inh_by
+                            ):
                                 inh_by.append(rule._tx_class)
                                 # stop after first added/found type
                                 return True
                         else:
-                            is_ordered_choice = type(rule) is OrderedChoice
+                            is_ordered_choice = isinstance(rule, OrderedChoice)
                             inh_added = False
                             for r in rule.nodes:
                                 inh_added |= _add_reffered_classes(r, inh_by)
@@ -400,11 +545,9 @@ def _add_reffered_classes(rule, inh_by, start=False):
                 _determine_rule_type(cls)
 
     def _resolve_cls_refs(self, grammar_parser, model_parser):
-
         resolved_classes = {}
 
         def _resolve_cls(cls):
-
             if cls in resolved_classes:
                 return resolved_classes[cls]
 
@@ -414,11 +557,14 @@ def _resolve_cls(cls):
             if isinstance(cls, ClassCrossRef):
                 try:
                     cls = metamodel[cls.cls_name]
-                except KeyError:
+                except KeyError as e:
                     line, col = grammar_parser.pos_to_linecol(cls.position)
                     raise TextXSemanticError(
-                        'Unknown class/rule "{}".'.format(cls.cls_name),
-                        line=line, col=col, filename=metamodel.file_name)
+                        f'Unknown class/rule "{cls.cls_name}".',
+                        line=line,
+                        col=col,
+                        filename=metamodel.file_name,
+                    ) from e
             resolved_classes[to_resolve] = cls
 
             if cls._tx_type == RULE_ABSTRACT:
@@ -428,7 +574,6 @@ def _resolve_cls(cls):
                     cls._tx_inh_by[idx] = inh
 
             else:
-
                 # If this is not abstract class than it must be common or
                 # match. Resolve referred classes.
                 for attr in cls._tx_attrs.values():
@@ -436,8 +581,10 @@ def _resolve_cls(cls):
 
                     # If target cls is of a base type or match rule
                     # then attr can not be a reference.
-                    if attr.cls.__name__ in BASE_TYPE_NAMES \
-                            or attr.cls._tx_type == RULE_MATCH:
+                    if (
+                        attr.cls.__name__ in BASE_TYPE_NAMES
+                        or attr.cls._tx_type == RULE_MATCH
+                    ):
                         attr.ref = False
                         attr.cont = True
                         attr.is_base_type = True
@@ -448,11 +595,16 @@ def _resolve_cls(cls):
                     if grammar_parser.debug:
                         grammar_parser.dprint(
                             "Resolved attribute {}:{}[cls={}, cont={}, "
-                            "ref={}, mult={}, pos={}]"
-                            .format(cls.__name__, attr.name,
-                                    attr.cls.__name__,
-                                    attr.cont, attr.ref, attr.mult,
-                                    attr.position))
+                            "ref={}, mult={}, pos={}]".format(
+                                cls.__name__,
+                                attr.name,
+                                attr.cls.__name__,
+                                attr.cont,
+                                attr.ref,
+                                attr.mult,
+                                attr.position,
+                            )
+                        )
 
             return cls
 
@@ -474,7 +626,7 @@ def visit_reference_stm(self, node, children):
         self.metamodel.referenced_languages[language_alias] = language_name
 
     def visit_grammar_to_import(self, node, children):
-        return text(node)
+        return str(node)
 
     def visit_textx_rule(self, node, children):
         if len(children) > 2:
@@ -483,17 +635,17 @@ def visit_textx_rule(self, node, children):
             rule_name, root_rule = children
             rule_params = {}
 
-        if root_rule.rule_name.startswith('__asgn') or \
-                ((isinstance(root_rule, Match) or
-                  isinstance(root_rule, RuleCrossRef))
-                 and rule_params):
+        if root_rule.rule_name.startswith("__asgn") or (
+            isinstance(root_rule, (Match, RuleCrossRef)) and rule_params
+        ):
             # If it is assignment node it must be kept because it could be
             # e.g. single assignment in the rule.
             # Also, handle a special case where rule consists only of a single
             # match or single rule reference and there are rule modifiers
             # defined.
-            root_rule = Sequence(nodes=[root_rule], rule_name=rule_name,
-                                 root=True, **rule_params)
+            root_rule = Sequence(
+                nodes=[root_rule], rule_name=rule_name, root=True, **rule_params
+            )
         else:
             if not isinstance(root_rule, RuleCrossRef):
                 # Promote rule node to root node.
@@ -513,7 +665,6 @@ def visit_textx_rule(self, node, children):
         # Update multiplicities of attributes based on their parent
         # expressions.
         def _update_attr_multiplicities(rule, oc_branch_set, mult=MULT_ONE):
-
             if isinstance(rule, RuleCrossRef):
                 return
 
@@ -524,20 +675,20 @@ def _update_attr_multiplicities(rule, oc_branch_set, mult=MULT_ONE):
             else:
                 if isinstance(rule, OneOrMore):
                     mult = MULT_ONEORMORE
-                elif isinstance(rule, ZeroOrMore):
-                    if mult != MULT_ONEORMORE:
-                        mult = MULT_ZEROORMORE
+                elif isinstance(rule, ZeroOrMore) and mult != MULT_ONEORMORE:
+                    mult = MULT_ZEROORMORE
 
-                if rule.rule_name.startswith('__asgn'):
+                if rule.rule_name.startswith("__asgn"):
                     cls_attr = cls._tx_attrs[rule._attr_name]
                     if mult in [MULT_ZEROORMORE, MULT_ONEORMORE]:
-                        if rule.rule_name == '__asgn_optional':
+                        if rule.rule_name == "__asgn_optional":
                             raise TextXSemanticError(
-                                'Can\'t use bool assignment '
-                                'inside repetition in rule "{}" at {}.'
-                                .format(rule_name,
-                                        self.grammar_parser
-                                        .pos_to_linecol(node.position)))
+                                "Can't use bool assignment "
+                                'inside repetition in rule "{}" at {}.'.format(
+                                    rule_name,
+                                    self.grammar_parser.pos_to_linecol(node.position),
+                                )
+                            )
                         if mult_lt(cls_attr.mult, mult):
                             cls_attr.mult = mult
                     # If multiplicity is not "many" still we can have
@@ -563,13 +714,11 @@ def visit_rule_name(self, node, children):
         rule_name = str(node)
 
         if self.debug:
-            self.dprint("Creating class: {}".format(rule_name))
+            self.dprint(f"Creating class: {rule_name}")
 
         # If a class is given by the user use it. Else, create new class.
         if self.metamodel.user_classes_provider is not None:
-            cls = self.metamodel.user_classes_provider(
-                rule_name
-            )
+            cls = self.metamodel.user_classes_provider(rule_name)
             if cls is not None:
                 self.metamodel.user_classes[rule_name] = cls
         else:
@@ -577,14 +726,15 @@ def visit_rule_name(self, node, children):
 
         if cls is not None:
             if rule_name in self.metamodel._used_rule_names_for_user_classes:
-                raise TextXSemanticError("redefined imported rule"
-                                         + " {}".format(rule_name)
-                                         + " cannot be replaced by a user class")
+                raise TextXSemanticError(
+                    "redefined imported rule"
+                    + f" {rule_name}"
+                    + " cannot be replaced by a user class"
+                )
             self.metamodel._used_rule_names_for_user_classes.add(rule_name)
 
             # Initialize special attributes
-            self.metamodel._init_class(cls, None, node.position,
-                                       external_attributes=True)
+            self.metamodel._init_class(cls, None, node.position, external_attributes=True)
         else:
             # Create class to collect attributes. At this time PEG rule
             # is not known.
@@ -601,17 +751,18 @@ def visit_rule_name(self, node, children):
     def visit_rule_params(self, node, children):
         params = {}
         for name, value in children:
-            if name not in ['skipws', 'ws', 'split']:
+            if name not in ["skipws", "ws", "split"]:
                 raise TextXSyntaxError(
-                    'Invalid rule param "{}" at {}.'
-                    .format(name,
-                            self.grammar_parser.pos_to_linecol(node.position)))
+                    'Invalid rule param "{}" at {}.'.format(
+                        name, self.grammar_parser.pos_to_linecol(node.position)
+                    )
+                )
 
-            if name == 'split' and not isinstance(value, str):
+            if name == "split" and not isinstance(value, str):
                 raise TextXError("param split requires a string parameter")
-            if name == 'split' and len(value)==0:
+            if name == "split" and len(value) == 0:
                 raise TextXError("param split requires a non-empty string parameter")
-            if name == 'ws' and '\\' in value:
+            if name == "ws" and "\\" in value:
                 new_value = ""
                 if "\\n" in value:
                     new_value += "\n"
@@ -633,18 +784,17 @@ def visit_rule_param(self, node, children):
         else:
             param_name = children[0]
             param_value = True
-            if param_name.startswith('no'):
+            if param_name.startswith("no"):
                 param_name = param_name[2:]
                 param_value = False
 
         if self.debug:
-            self.dprint("TextX rule param: {}, {}".format(param_name,
-                                                          param_value))
+            self.dprint(f"TextX rule param: {param_name}, {param_value}")
 
         return (param_name, param_value)
 
     def visit_rule_ref(self, node, children):
-        rule_name = text(node)
+        rule_name = str(node)
         # Here a name of the meta-class (rule) is expected but to support
         # forward referencing we are postponing resolving to second_pass.
         return RuleCrossRef(rule_name, rule_name, node.position, None)
@@ -669,7 +819,7 @@ def visit_choice(self, node, children):
     def visit_expression(self, node, children):
         if len(children) == 1:
             return children[0]
-        if children[0] == '!':
+        if children[0] == "!":
             return Not(nodes=[children[1]])
         else:
             return And(nodes=[children[1]])
@@ -679,12 +829,12 @@ def visit_repeat_modifiers(self, node, children):
         for modifier in children:
             if isinstance(modifier, Match):
                 # Separator
-                modifier.rule_name = 'sep'
-                modifiers['sep'] = modifier
-            elif type(modifier) == tuple:
-                modifiers['multiplicity'] = modifier
+                modifier.rule_name = "sep"
+                modifiers["sep"] = modifier
+            elif isinstance(modifier, tuple):
+                modifiers["multiplicity"] = modifier
             else:
-                modifiers['eolterm'] = True
+                modifiers["eolterm"] = True
         return (modifiers, node.position)
 
     def visit_repeat_operator(self, node, children):
@@ -701,7 +851,7 @@ def visit_repeatable_expr(self, node, children):
                 repeat_op = children[1]
                 suppress = True
             else:
-                if children[1] == '-':
+                if children[1] == "-":
                     suppress = True
                 else:
                     repeat_op = children[1]
@@ -713,11 +863,11 @@ def visit_repeatable_expr(self, node, children):
                     repeat_op = repeat_op[0]
                     modifiers = None
 
-                if repeat_op == '?':
+                if repeat_op == "?":
                     rule = Optional(nodes=[expr])
-                elif repeat_op == '*':
+                elif repeat_op == "*":
                     rule = ZeroOrMore(nodes=[expr])
-                elif repeat_op == '+':
+                elif repeat_op == "+":
                     rule = OneOrMore(nodes=[expr])
                 else:
                     rule = UnorderedGroup(nodes=expr.nodes)
@@ -726,18 +876,20 @@ def visit_repeatable_expr(self, node, children):
                     modifiers, position = modifiers
                     # Sanity check. Modifiers do not make
                     # sense for ? operator at the moment.
-                    if repeat_op == '?':
-                        line, col = \
-                            self.grammar_parser.pos_to_linecol(position)
+                    if repeat_op == "?":
+                        line, col = self.grammar_parser.pos_to_linecol(position)
                         raise TextXSyntaxError(
-                            'Modifiers are not allowed for "?" operator at {}'
-                            .format(text((line, col))), line, col)
+                            "Modifiers are not allowed "
+                            f'for "?" operator at {(line, col)}',
+                            line,
+                            col,
+                        )
 
                     # Separator modifier
-                    rule.sep = modifiers.get('sep', None)
+                    rule.sep = modifiers.get("sep", None)
 
                     # End of line termination modifier
-                    if 'eolterm' in modifiers:
+                    if "eolterm" in modifiers:
                         rule.eolterm = True
 
         # Mark rule for suppression
@@ -768,33 +920,34 @@ def visit_assignment(self, node, children):
         target_cls = None
 
         if self.debug:
-            self.dprint("Processing assignment {}{}..."
-                        .format(attr_name, op))
+            self.dprint(f"Processing assignment {attr_name}{op}...")
 
         if self.debug:
-            self.dprint("Creating attribute {}:{}".format(cls.__name__,
-                                                          attr_name))
-            self.dprint("Assignment operation = {}".format(op))
+            self.dprint(f"Creating attribute {cls.__name__}:{attr_name}")
+            self.dprint(f"Assignment operation = {op}")
 
         if attr_name in cls._tx_attrs:
             # If attribute already exists in the metamodel it is
             # multiple assignment to the same attribute.
 
             # Cannot use operator ?= on multiple assignments
-            if op == '?=':
+            if op == "?=":
                 line, col = self.grammar_parser.pos_to_linecol(node.position)
                 raise TextXSemanticError(
                     'Cannot use "?=" operator on multiple'
-                    ' assignments for attribute "{}" at {}'
-                    .format(attr_name, (line, col)), line, col)
+                    f' assignments for attribute "{attr_name}" at {(line, col)}',
+                    line,
+                    col,
+                )
 
             cls_attr = cls._tx_attrs[attr_name]
         else:
-            cls_attr = self.metamodel._new_cls_attr(cls, name=attr_name,
-                                                    position=node.position)
+            cls_attr = self.metamodel._new_cls_attr(
+                cls, name=attr_name, position=node.position
+            )
 
         # Keep track of metaclass references and containments
-        if type(rhs_rule) is tuple and rhs_rule[0] == "obj_ref":
+        if isinstance(rhs_rule, tuple) and rhs_rule[0] == "obj_ref":
             cls_attr.cont = False
             cls_attr.ref = True
             # Override rhs by its PEG rule for further processing
@@ -806,23 +959,23 @@ def visit_assignment(self, node, children):
             target_cls = rhs_rule.cls
 
         base_rule_name = rhs_rule.rule_name
-        if op == '+=':
+        if op == "+=":
             assignment_rule = OneOrMore(
-                nodes=[rhs_rule],
-                rule_name='__asgn_oneormore', root=True)
+                nodes=[rhs_rule], rule_name="__asgn_oneormore", root=True
+            )
             cls_attr.mult = MULT_ONEORMORE
-        elif op == '*=':
+        elif op == "*=":
             assignment_rule = ZeroOrMore(
-                nodes=[rhs_rule],
-                rule_name='__asgn_zeroormore', root=True)
+                nodes=[rhs_rule], rule_name="__asgn_zeroormore", root=True
+            )
             if cls_attr.mult is not MULT_ONEORMORE:
                 cls_attr.mult = MULT_ZEROORMORE
-        elif op == '?=':
+        elif op == "?=":
             assignment_rule = Optional(
-                nodes=[rhs_rule],
-                rule_name='__asgn_optional', root=True)
+                nodes=[rhs_rule], rule_name="__asgn_optional", root=True
+            )
             cls_attr.mult = MULT_OPTIONAL
-            base_rule_name = 'BOOL'
+            base_rule_name = "BOOL"
 
             # ?= assignment should have default value of False.
             # so we shall mark it as such.
@@ -830,71 +983,80 @@ def visit_assignment(self, node, children):
 
         else:
             assignment_rule = Sequence(
-                nodes=[rhs_rule],
-                rule_name='__asgn_plain', root=True)
+                nodes=[rhs_rule], rule_name="__asgn_plain", root=True
+            )
 
         # Modifiers
         if modifiers:
             modifiers, position = modifiers
             # Sanity check. Modifiers do not make
             # sense for ?= and = operator at the moment.
-            if op == '?=' or op == '=':
+            if op == "?=" or op == "=":
                 line, col = self.grammar_parser.pos_to_linecol(position)
                 raise TextXSyntaxError(
-                    'Modifiers are not allowed for "{}" operator at {}'
-                    .format(op, text((line, col))), line, col,
-                    filename=self.metamodel.file_name)
+                    f'Modifiers are not allowed for "{op}" operator at {(line, col)}',
+                    line,
+                    col,
+                    filename=self.metamodel.file_name,
+                )
 
             # Separator modifier
-            assignment_rule.sep = modifiers.get('sep', None)
+            assignment_rule.sep = modifiers.get("sep", None)
 
             # End of line termination modifier
-            if 'eolterm' in modifiers:
+            if "eolterm" in modifiers:
                 assignment_rule.eolterm = True
 
         if target_cls:
             attr_type = target_cls
         else:
             # Use STRING as default attr class
-            attr_type = base_rule_name if base_rule_name else 'STRING'
+            attr_type = base_rule_name if base_rule_name else "STRING"
         if not cls_attr.cls:
-            cls_attr.cls = ClassCrossRef(cls_name=attr_type,
-                                         position=node.position)
+            cls_attr.cls = ClassCrossRef(cls_name=attr_type, position=node.position)
         else:
             # cls cross ref might already be set in case of multiple assignment
             # to the same attribute. If types are not the same we shall use
             # OBJECT as generic type.
             if cls_attr.cls.cls_name != attr_type:
-                cls_attr.cls.cls_name = 'OBJECT'
+                cls_attr.cls.cls_name = "OBJECT"
 
         if self.debug:
-            self.dprint("Created attribute {}:{}[cls={}, cont={}, "
-                        "ref={}, mult={}, pos={}]"
-                        .format(cls.__name__, attr_name, cls_attr.cls.cls_name,
-                                cls_attr.cont, cls_attr.ref, cls_attr.mult,
-                                cls_attr.position))
+            self.dprint(
+                "Created attribute {}:{}[cls={}, cont={}, "
+                "ref={}, mult={}, pos={}]".format(
+                    cls.__name__,
+                    attr_name,
+                    cls_attr.cls.cls_name,
+                    cls_attr.cont,
+                    cls_attr.ref,
+                    cls_attr.mult,
+                    cls_attr.position,
+                )
+            )
 
         assignment_rule._attr_name = attr_name
-        assignment_rule._exp_str = attr_name    # For nice error reporting
+        assignment_rule._exp_str = attr_name  # For nice error reporting
         return assignment_rule
 
     def visit_str_match(self, node, children):
         try:
             to_match = children[0][1:-1]
-            if '\\' in to_match:
+            if "\\" in to_match:
                 to_match = decode_escapes(to_match)
 
         except IndexError:
-            to_match = ''
+            to_match = ""
 
         # Support for autokwd metamodel param.
         if self.metamodel.autokwd:
             match = self.keyword_regex.match(to_match)
             if match and match.span() == (0, len(to_match)):
                 regex_match = RegExMatch(
-                    r'{}\b'.format(to_match),
+                    rf"{to_match}\b",
                     ignore_case=self.metamodel.ignore_case,
-                    str_repr=to_match)
+                    str_repr=to_match,
+                )
                 regex_match.compile()
                 return regex_match
         return StrMatch(to_match, ignore_case=self.metamodel.ignore_case)
@@ -902,13 +1064,12 @@ def visit_str_match(self, node, children):
     def visit_re_match(self, node, children):
         to_match = node.extra_info.group(1)
         # print("**** visit_re_match, to_match == '{}'".format(to_match))
-        regex = RegExMatch(to_match,
-                           ignore_case=self.metamodel.ignore_case)
+        regex = RegExMatch(to_match, ignore_case=self.metamodel.ignore_case)
         try:
             regex.compile()
         except Exception as e:
             line, col = self.grammar_parser.pos_to_linecol(node[1].position)
-            raise TextXSyntaxError(text(e), line, col)
+            raise TextXSyntaxError(str(e), line, col) from e
         return regex
 
     def visit_obj_ref(self, node, children):
@@ -919,15 +1080,17 @@ def visit_obj_ref(self, node, children):
         if class_name in BASE_TYPE_NAMES:
             line, col = self.grammar_parser.pos_to_linecol(node.position)
             raise TextXSemanticError(
-                'Primitive type instances can not be referenced at {}.'
-                .format((line, col)), line, col)
+                f"Primitive type instances can not be referenced at {(line, col)}.",
+                line,
+                col,
+            )
         if len(children) > 1:
             rule_name = children[2]
             if len(children) > 3:
                 rrel_tree = children[3]
         else:
             # Default rule for matching obj cross-refs
-            rule_name = 'ID'
+            rule_name = "ID"
         return ("obj_ref", RuleCrossRef(rule_name, class_name, node.position, rrel_tree))
 
     def visit_integer(self, node, children):
@@ -957,8 +1120,8 @@ def language_from_str(language_def, metamodel, file_name):
         Parser for the new language.
     """
 
-    if type(language_def) is not text:
-        raise TextXError("textX accepts only unicode strings.")
+    if not isinstance(language_def, str):
+        raise TextXError("textX accepts only strings.")
 
     if metamodel.debug:
         metamodel.dprint("*** PARSING LANGUAGE DEFINITION ***")
@@ -969,12 +1132,15 @@ def language_from_str(language_def, metamodel, file_name):
     else:
         # Create parser for TextX grammars using
         # the arpeggio grammar specified in this module
-        parser = ParserPython(textx_model, comment_def=comment,
-                              ignore_case=False,
-                              reduce_tree=False,
-                              memoization=metamodel.memoization,
-                              debug=metamodel.debug,
-                              file=metamodel.file)
+        parser = ParserPython(
+            textx_model,
+            comment_def=comment,
+            ignore_case=False,
+            reduce_tree=False,
+            memoization=metamodel.memoization,
+            debug=metamodel.debug,
+            file=metamodel.file,
+        )
 
         # Cache it for subsequent calls
         textX_parsers[metamodel.debug] = parser
@@ -984,14 +1150,13 @@ def language_from_str(language_def, metamodel, file_name):
         parse_tree = parser.parse(language_def, file_name)
     except NoMatch as e:
         e.eval_attrs()
-        raise TextXSyntaxError(e.message, e.line, e.col,
-                               filename=e.parser.file_name,
-                               context=e.context)
+        raise TextXSyntaxError(
+            e.message, e.line, e.col, filename=e.parser.file_name, context=e.context
+        ) from e
 
     # Construct new parser and meta-model based on the given language
     # description.
-    lang_parser = visit_parse_tree(parse_tree,
-                                   TextXVisitor(parser, metamodel))
+    lang_parser = visit_parse_tree(parse_tree, TextXVisitor(parser, metamodel))
 
     # Meta-model is constructed. Validate its semantics.
     metamodel.validate()
@@ -1004,6 +1169,7 @@ def language_from_str(language_def, metamodel, file_name):
         # Create dot file for debugging purposes
         PMDOTExporter().exportFile(
             lang_parser.parser_model,
-            "{}_parser_model.dot".format(metamodel.rootcls.__name__))
+            f"{metamodel.rootcls.__name__}_parser_model.dot",
+        )
 
     return lang_parser
diff --git a/textx/metamodel.py b/textx/metamodel.py
index 6ad4230f8..123476f6c 100644
--- a/textx/metamodel.py
+++ b/textx/metamodel.py
@@ -1,30 +1,42 @@
 """
 Meta-model construction.
 """
-from __future__ import absolute_import
 import codecs
 import os
-import sys
 import warnings
-from os.path import join, abspath, dirname
 from collections import OrderedDict
+from os.path import abspath, dirname, join
+
 from arpeggio import DebugPrinter
-from textx.lang import language_from_str, python_type, BASE_TYPE_NAMES, ID, \
-    BOOL, INT, FLOAT, STRICTFLOAT, STRING, NUMBER, BASETYPE, OBJECT
-from textx.const import MULT_ONE, MULT_ZEROORMORE, MULT_ONEORMORE, \
-    RULE_MATCH, RULE_ABSTRACT
+
+from textx.const import (
+    MULT_ONE,
+    MULT_ONEORMORE,
+    MULT_ZEROORMORE,
+    RULE_ABSTRACT,
+    RULE_MATCH,
+)
 from textx.exceptions import TextXError
-from .registration import LanguageDesc, metamodel_for_language
-from .model_params import ModelParams, ModelParamDefinitions
+from textx.lang import (
+    BASE_TYPE_NAMES,
+    BASETYPE,
+    BOOL,
+    FLOAT,
+    ID,
+    INT,
+    NUMBER,
+    OBJECT,
+    STRICTFLOAT,
+    STRING,
+    language_from_str,
+    python_type,
+)
 from textx.scoping.rrel import create_rrel_scope_provider
 
-if sys.version < '3':
-    text = unicode  # noqa
-else:
-    text = str
-
+from .model_params import ModelParamDefinitions, ModelParams
+from .registration import LanguageDesc, metamodel_for_language
 
-__all__ = ['metamodel_from_str', 'metamodel_from_file']
+__all__ = ["metamodel_from_str", "metamodel_from_file"]
 
 
 def metamodel_from_str(lang_desc, metamodel=None, **kwargs):
@@ -43,7 +55,7 @@ def metamodel_from_str(lang_desc, metamodel=None, **kwargs):
     if not metamodel:
         metamodel = TextXMetaModel(**kwargs)
 
-    file_name = kwargs.get('file_name')
+    file_name = kwargs.get("file_name")
 
     language_from_str(lang_desc, metamodel, file_name)
 
@@ -61,17 +73,15 @@ def metamodel_from_file(file_name, **kwargs):
         file_name(str): The name of the file with textX language description.
         other params: See metamodel_from_str.
     """
-    with codecs.open(file_name, 'r', 'utf-8') as f:
+    with codecs.open(file_name, "r", "utf-8") as f:
         lang_desc = f.read()
 
-    metamodel = metamodel_from_str(lang_desc=lang_desc,
-                                   file_name=file_name,
-                                   **kwargs)
+    metamodel = metamodel_from_str(lang_desc=lang_desc, file_name=file_name, **kwargs)
 
     return metamodel
 
 
-class MetaAttr(object):
+class MetaAttr:
     """
     A metaclass for attribute description.
 
@@ -87,8 +97,17 @@ class MetaAttr(object):
         position(int): A position in the input string where attribute is
             defined.
     """
-    def __init__(self, name, cls=None, mult=MULT_ONE, cont=True, ref=False,
-                 bool_assignment=False, position=0):
+
+    def __init__(
+        self,
+        name,
+        cls=None,
+        mult=MULT_ONE,
+        cont=True,
+        ref=False,
+        bool_assignment=False,
+        position=0,
+    ):
         self.name = name
         self.cls = cls
         self.mult = mult
@@ -163,21 +182,33 @@ class for that rule name or None.
             replaced with the group value, if they have exactly one group.
     """
 
-    def __init__(self, file_name=None, classes=None, builtins=None,
-                 builtin_models=None, auto_init_attributes=True,
-                 ignore_case=False, skipws=True, ws=None, autokwd=False,
-                 memoization=False, textx_tools_support=False,
-                 use_regexp_group=False, **kwargs):
+    def __init__(
+        self,
+        file_name=None,
+        classes=None,
+        builtins=None,
+        builtin_models=None,
+        auto_init_attributes=True,
+        ignore_case=False,
+        skipws=True,
+        ws=None,
+        autokwd=False,
+        memoization=False,
+        textx_tools_support=False,
+        use_regexp_group=False,
+        **kwargs,
+    ):
         # evaluate optional parameter "global_repository"
         global_repository = kwargs.pop("global_repository", False)
         if global_repository:
             from textx.scoping import GlobalModelRepository
+
             if isinstance(global_repository, GlobalModelRepository):
                 self._tx_model_repository = global_repository
             else:
                 self._tx_model_repository = GlobalModelRepository()
 
-        super(TextXMetaModel, self).__init__(**kwargs)
+        super().__init__(**kwargs)
 
         self.model_param_defs = ModelParamDefinitions()
         self.model_param_defs.add("project_root", "the project root path")
@@ -213,12 +244,11 @@ def __init__(self, file_name=None, classes=None, builtins=None,
 
         # Match rule and base type conversion callables
         self._default_obj_processors = {
-            'BOOL': lambda x: x == '1' or x.lower() == 'true',
-            'INT': lambda x: int(x),
-            'FLOAT': lambda x: float(x),
-            'STRICTFLOAT': lambda x: float(x),
-            'STRING': lambda x: x[1:-1].replace(r'\"',
-                                                r'"').replace(r"\'", "'"),
+            "BOOL": lambda x: x == "1" or x.lower() == "true",
+            "INT": lambda x: int(x),
+            "FLOAT": lambda x: float(x),
+            "STRICTFLOAT": lambda x: float(x),
+            "STRING": lambda x: x[1:-1].replace(r"\"", r'"').replace(r"\'", "'"),
         }
 
         # Registered object processors (use _default_obj_processors)
@@ -238,23 +268,27 @@ def __init__(self, file_name=None, classes=None, builtins=None,
         self.referenced_languages = {}
 
         # Create new namespace for BASETYPE classes
-        self._enter_namespace('__base__')
+        self._enter_namespace("__base__")
 
         # Base types hierarchy should exist in each meta-model
-        base_id = self._new_class('ID', ID, 0)
-        base_string = self._new_class('STRING', STRING, 0)
-        base_bool = self._new_class('BOOL', BOOL, 0)
-        base_int = self._new_class('INT', INT, 0)
-        base_float = self._new_class('FLOAT', FLOAT, 0)
-        base_strictfloat = self._new_class('STRICTFLOAT', STRICTFLOAT, 0)
-        base_number = self._new_class('NUMBER', NUMBER, 0,
-                                      inherits=[base_strictfloat, base_int])
-        base_type = self._new_class('BASETYPE', BASETYPE, 0,
-                                    inherits=[base_number, base_float,
-                                              base_bool, base_id,
-                                              base_string])
-        self._new_class('OBJECT', OBJECT, 0, inherits=[base_type],
-                        rule_type=RULE_ABSTRACT)
+        base_id = self._new_class("ID", ID, 0)
+        base_string = self._new_class("STRING", STRING, 0)
+        base_bool = self._new_class("BOOL", BOOL, 0)
+        base_int = self._new_class("INT", INT, 0)
+        base_float = self._new_class("FLOAT", FLOAT, 0)
+        base_strictfloat = self._new_class("STRICTFLOAT", STRICTFLOAT, 0)
+        base_number = self._new_class(
+            "NUMBER", NUMBER, 0, inherits=[base_strictfloat, base_int]
+        )
+        base_type = self._new_class(
+            "BASETYPE",
+            BASETYPE,
+            0,
+            inherits=[base_number, base_float, base_bool, base_id, base_string],
+        )
+        self._new_class(
+            "OBJECT", OBJECT, 0, inherits=[base_type], rule_type=RULE_ABSTRACT
+        )
 
         self._leave_namespace()
 
@@ -283,7 +317,7 @@ def _namespace_for_file_name(self, file_name):
         file_name = os.path.abspath(file_name)
         p = os.path
         q = p.splitext(p.relpath(file_name, start=self.root_path))[0]
-        return '.'.join(p.split(q)[1:])
+        return ".".join(p.split(q)[1:])
 
     def _enter_namespace(self, namespace_name):
         """
@@ -295,8 +329,7 @@ def _enter_namespace(self, namespace_name):
 
             # BASETYPE namespace is imported in each namespace
             # as the first namespace to be searched.
-            self._imported_namespaces[namespace_name] = \
-                [self.namespaces['__base__']]
+            self._imported_namespaces[namespace_name] = [self.namespaces["__base__"]]
 
         self._namespace_stack.append(namespace_name)
 
@@ -315,35 +348,41 @@ def _new_import(self, import_name):
         """
 
         # Import can't be used if meta-model is loaded from string
-        assert self.root_path is not None, \
-            '"import" statement can not be used if meta-model is ' \
-            'loaded from string.'
+        assert self.root_path is not None, (
+            '"import" statement can not be used if meta-model is ' "loaded from string."
+        )
 
         # Find the absolute file name of the import based on the relative
         # import_name and current namespace
         current_namespace = self._namespace_stack[-1]
-        if '.' in current_namespace:
-            root_namespace = current_namespace.rsplit('.', 1)[0]
-            import_name = "%s.%s" % (root_namespace, import_name)
+        if "." in current_namespace:
+            root_namespace = current_namespace.rsplit(".", 1)[0]
+            import_name = f"{root_namespace}.{import_name}"
 
-        import_file_name = "%s.tx" % os.path.join(self.root_path,
-                                                  *import_name.split("."))
+        import_file_name = "%s.tx" % os.path.join(self.root_path, *import_name.split("."))
 
         if import_name not in self.namespaces:
             self._enter_namespace(import_name)
             if self.debug:
-                self.dprint("*** IMPORTING FILE: %s" % import_file_name)
+                self.dprint(f"*** IMPORTING FILE: {import_file_name}")
             metamodel_from_file(import_file_name, metamodel=self)
             self._leave_namespace()
 
         # Add the import to the imported_namespaces for current namespace
         # so that resolving of current grammar searches imported grammars
         # in the order of import
-        self._imported_namespaces[current_namespace].append(
-            self.namespaces[import_name])
-
-    def _new_class(self, name, peg_rule, position, position_end=None,
-                   inherits=None, root=False, rule_type=RULE_MATCH):
+        self._imported_namespaces[current_namespace].append(self.namespaces[import_name])
+
+    def _new_class(
+        self,
+        name,
+        peg_rule,
+        position,
+        position_end=None,
+        inherits=None,
+        root=False,
+        rule_type=RULE_MATCH,
+    ):
         """
         Creates a new class with the given name in the current namespace.
         Args:
@@ -357,12 +396,10 @@ def _new_class(self, name, peg_rule, position, position_end=None,
         """
 
         class TextXMetaClass(type):
-
             def __repr__(cls):
-                return '<textx:{} class at {}>'.format(cls._tx_fqn,
-                                                       id(cls))
+                return f"<textx:{cls._tx_fqn} class at {id(cls)}>"
 
-        class TextXClass(object, metaclass=TextXMetaClass):
+        class TextXClass(metaclass=TextXMetaClass):
             """
             Dynamically created class. Each textX rule will result in
             creating one Python class with the type name of the rule.
@@ -390,23 +427,29 @@ def __repr__(self):
                 """
                 Used for TextXClass bellow.
                 """
-                if hasattr(self, 'name'):
-                    return "<{}:{}>".format(name, self.name)
+                if hasattr(self, "name"):
+                    return f"<{name}:{self.name}>"
                 else:
-                    return "<textx:{} instance at {}>"\
-                        .format(self._tx_fqn, hex(id(self)))
+                    return f"<textx:{self._tx_fqn} instance at {hex(id(self))}>"
 
         cls = TextXClass
         cls.__name__ = name
 
-        self._init_class(cls, peg_rule, position, position_end, inherits, root,
-                         rule_type)
+        self._init_class(cls, peg_rule, position, position_end, inherits, root, rule_type)
 
         return cls
 
-    def _init_class(self, cls, peg_rule, position, position_end=None,
-                    inherits=None, root=False, rule_type=RULE_MATCH,
-                    external_attributes=False):
+    def _init_class(
+        self,
+        cls,
+        peg_rule,
+        position,
+        position_end=None,
+        inherits=None,
+        root=False,
+        rule_type=RULE_MATCH,
+        external_attributes=False,
+    ):
         """
         Setup meta-class special attributes, namespaces etc. This is called
         both for textX created classes as well as user classes.
@@ -422,8 +465,7 @@ def _init_class(self, cls, peg_rule, position, position_end=None,
 
         cls._tx_position = position
 
-        cls._tx_position_end = \
-            position if position_end is None else position_end
+        cls._tx_position_end = position if position_end is None else position_end
 
         # The type of the rule this meta-class results from.
         # There are three rule types: common, abstract and match
@@ -451,10 +493,10 @@ def _cls_fqn(self, cls):
         and the class name.
         """
         ns = self._namespace_stack[-1]
-        if ns in ['__base__', None]:
+        if ns in ["__base__", None]:
             return cls.__name__
         else:
-            return ns + '.' + cls.__name__
+            return ns + "." + cls.__name__
 
     def _init_obj_attrs(self, obj):
         """
@@ -469,8 +511,7 @@ def _init_obj_attrs(self, obj):
             elif attr.cls.__name__ in BASE_TYPE_NAMES:
                 # Instantiate base python type
                 if self.auto_init_attributes:
-                    setattr(obj, attr.name,
-                            python_type(attr.cls.__name__)())
+                    setattr(obj, attr.name, python_type(attr.cls.__name__)())
                 else:
                     # See https://github.com/textX/textX/issues/11
                     if attr.bool_assignment:
@@ -487,11 +528,19 @@ def _init_obj_attrs(self, obj):
                 # Reference to other obj
                 setattr(obj, attr.name, None)
 
-    def _new_cls_attr(self, clazz, name, cls=None, mult=MULT_ONE, cont=True,
-                      ref=False, bool_assignment=False, position=0):
+    def _new_cls_attr(
+        self,
+        clazz,
+        name,
+        cls=None,
+        mult=MULT_ONE,
+        cont=True,
+        ref=False,
+        bool_assignment=False,
+        position=0,
+    ):
         """Creates new meta attribute of this class."""
-        attr = MetaAttr(name, cls, mult, cont, ref, bool_assignment,
-                        position)
+        attr = MetaAttr(name, cls, mult, cont, ref, bool_assignment, position)
         clazz._tx_attrs[name] = attr
         return attr
 
@@ -517,6 +566,7 @@ def process(self, value, _type, filename, col, line):
             return self._obj_processors.get(_type, lambda x: x)(value)
         except Exception as e:
             from textx.exceptions import TextXError
+
             if isinstance(e, TextXError):
                 if e.col is None:
                     e.col = col
@@ -543,12 +593,13 @@ def validate_user_classes(self):
         imported ones).
         """
         from textx.exceptions import TextXSemanticError
+
         for user_class in self.user_classes.values():
             if user_class.__name__ not in self._used_rule_names_for_user_classes:
                 # It is not a user class used in the grammar
                 raise TextXSemanticError(
-                    "{} class is not used in the grammar".format(
-                        user_class.__name__))
+                    f"{user_class.__name__} class is not used in the grammar"
+                )
 
     def __getitem__(self, name):
         """
@@ -558,7 +609,7 @@ def __getitem__(self, name):
         """
         if "." in name:
             # Name is fully qualified
-            namespace, name = name.rsplit('.', 1)
+            namespace, name = name.rsplit(".", 1)
             if namespace in self.referenced_languages:
                 language = self.referenced_languages[namespace]
                 referenced_metamodel = metamodel_for_language(language)
@@ -571,13 +622,11 @@ def __getitem__(self, name):
             if name in self._current_namespace:
                 return self._current_namespace[name]
 
-            for namespace in \
-                    self._imported_namespaces[self._namespace_stack[-1]]:
+            for namespace in self._imported_namespaces[self._namespace_stack[-1]]:
                 if name in namespace:
                     return namespace[name]
 
-            raise KeyError("{} metaclass does not exists in the meta-model "
-                           .format(name))
+            raise KeyError(f"{name} metaclass does not exists in the meta-model ")
 
     def __iter__(self):
         """
@@ -590,8 +639,7 @@ def __iter__(self):
             yield self._current_namespace[name]
 
         # Imported namespaces
-        for namespace in \
-                self._imported_namespaces[self._namespace_stack[-1]]:
+        for namespace in self._imported_namespaces[self._namespace_stack[-1]]:
             for name in namespace:
                 # yield class
                 yield namespace[name]
@@ -611,9 +659,15 @@ def __contains__(self, name):
     def _current_namespace(self):
         return self.namespaces[self._namespace_stack[-1]]
 
-    def model_from_str(self, model_str, file_name=None, debug=None,
-                       pre_ref_resolution_callback=None, encoding='utf-8',
-                       **kwargs):
+    def model_from_str(
+        self,
+        model_str,
+        file_name=None,
+        debug=None,
+        pre_ref_resolution_callback=None,
+        encoding="utf-8",
+        **kwargs,
+    ):
         """
         Instantiates model from the given string.
         :param pre_ref_resolution_callback: called before references are
@@ -624,53 +678,61 @@ def model_from_str(self, model_str, file_name=None, debug=None,
                 is set while executing pre_ref_resolution_callback (see
                 scoping.md)
         """
-        self.model_param_defs.check_params('from_str', **kwargs)
+        self.model_param_defs.check_params("from_str", **kwargs)
 
-        if type(model_str) is not text:
-            raise TextXError("textX accepts only unicode strings.")
+        if not isinstance(model_str, str):
+            raise TextXError("textX accepts only strings.")
 
         if file_name is None:
+
             def kwargs_callback(other_model):
-                if hasattr(other_model, '_tx_metamodel'):
+                if hasattr(other_model, "_tx_metamodel"):
                     other_model._tx_model_params = ModelParams(kwargs)
                 if pre_ref_resolution_callback:
                     pre_ref_resolution_callback(other_model)
 
             model = self._parser_blueprint.clone().get_model_from_str(
-                model_str, debug=debug,
-                pre_ref_resolution_callback=kwargs_callback)
+                model_str, debug=debug, pre_ref_resolution_callback=kwargs_callback
+            )
 
             for p in self._model_processors:
                 p(model, self)
         else:
             model = self.internal_model_from_file(
-                file_name, encoding, debug,
+                file_name,
+                encoding,
+                debug,
                 model_str=model_str,
                 pre_ref_resolution_callback=pre_ref_resolution_callback,
-                model_params=ModelParams(kwargs))
+                model_params=ModelParams(kwargs),
+            )
 
         return model
 
-    def model_from_file(self, file_name, encoding='utf-8', debug=None,
-                        **kwargs):
+    def model_from_file(self, file_name, encoding="utf-8", debug=None, **kwargs):
         self.model_param_defs.check_params(file_name, **kwargs)
 
         return self.internal_model_from_file(
-            file_name, encoding, debug,
-            model_params=ModelParams(kwargs))
+            file_name, encoding, debug, model_params=ModelParams(kwargs)
+        )
 
     def internal_model_from_file(
-            self, file_name, encoding='utf-8', debug=None,
-            pre_ref_resolution_callback=None, is_main_model=True,
-            model_str=None, model_params=None):
+        self,
+        file_name,
+        encoding="utf-8",
+        debug=None,
+        pre_ref_resolution_callback=None,
+        is_main_model=True,
+        model_str=None,
+        model_params=None,
+    ):
         """
         Instantiates model from the given file.
         :param pre_ref_resolution_callback: called before references are
                resolved. This can be useful to manage models distributed
                across files (scoping)
         """
-        assert model_params is not None, \
-            "model_params are required in all cases"
+        assert model_params is not None, "model_params are required in all cases"
         file_name = abspath(file_name)
         model = None
         callback = pre_ref_resolution_callback
@@ -678,13 +740,16 @@ def internal_model_from_file(
         if hasattr(self, "_tx_model_repository"):
             # metamodel has a global repo
             if not callback:
+
                 def _pre_ref_resolution_callback(other_model):
                     from textx.scoping import GlobalModelRepository
+
                     filename = other_model._tx_filename
                     assert filename
                     # print("METAMODEL PRE-CALLBACK => {}".format(filename))
                     other_model._tx_model_repository = GlobalModelRepository(
-                        self._tx_model_repository.all_models)
+                        self._tx_model_repository.all_models
+                    )
                     self._tx_model_repository.all_models[filename] = other_model
 
                 callback = _pre_ref_resolution_callback
@@ -692,7 +757,7 @@ def _pre_ref_resolution_callback(other_model):
                 model = self._tx_model_repository.all_models[file_name]
 
         def kwargs_callback(other_model):
-            if hasattr(other_model, '_tx_metamodel'):
+            if hasattr(other_model, "_tx_metamodel"):
                 other_model._tx_model_params = model_params
             if callback:
                 callback(other_model)
@@ -700,13 +765,17 @@ def kwargs_callback(other_model):
         if not model:
             # Read model from file
             if not model_str:
-                with codecs.open(file_name, 'r', encoding) as f:
+                with codecs.open(file_name, "r", encoding) as f:
                     model_str = f.read()
             # model not present (from global repo) -> load it
             model = self._parser_blueprint.clone().get_model_from_str(
-                model_str, file_name, debug=debug, encoding=encoding,
+                model_str,
+                file_name,
+                debug=debug,
+                encoding=encoding,
                 pre_ref_resolution_callback=kwargs_callback,
-                is_main_model=is_main_model)
+                is_main_model=is_main_model,
+            )
 
         for p in self._model_processors:
             p(model, self)
@@ -737,12 +806,13 @@ def register_obj_processors(self, obj_processors):
 
     @property
     def _tx_model_param_definitions(self):
-        warnings.warn(
-            '_tx_model_param_definitions is deprecated in favor of model_param_defs.')
+        warnings.warn(  # noqa: B028
+            "_tx_model_param_definitions is deprecated in favor of model_param_defs."
+        )
         return self.model_param_defs
 
 
-class TextXMetaMetaModel(object):
+class TextXMetaMetaModel:
     """
     A basic TextX meta-meta-model class that represents the meta-model of the
     textX meta-language.  Used to treat all languages in a consistent way.
@@ -756,7 +826,8 @@ def __init__(self):
     def metamodel(self):
         if self._metamodel is None:
             self._metamodel = metamodel_from_file(
-                join(abspath(dirname(__file__)), 'textx.tx'))
+                join(abspath(dirname(__file__)), "textx.tx")
+            )
         return self._metamodel
 
     def model_from_str(self, model_str, debug=None, **kwargs):
@@ -765,8 +836,7 @@ def model_from_str(self, model_str, debug=None, **kwargs):
         """
         return metamodel_from_str(model_str, debug=debug, **kwargs)
 
-    def model_from_file(self, file_name, encoding='utf-8', debug=None,
-                        **kwargs):
+    def model_from_file(self, file_name, encoding="utf-8", debug=None, **kwargs):
         """
         Instantiates meta-model (a.k.a. textX model) from the given file.
         """
@@ -779,8 +849,7 @@ def grammar_model_from_str(self, model_str, debug=None, **kwargs):
         """
         return self.metamodel.model_from_str(model_str, debug=debug, **kwargs)
 
-    def grammar_model_from_file(self, file_name, encoding='utf-8', debug=None,
-                                **kwargs):
+    def grammar_model_from_file(self, file_name, encoding="utf-8", debug=None, **kwargs):
         """
         Instantiates textX grammar model from the given file.  Used to
         programmatically inspect textX grammar.
@@ -788,8 +857,10 @@ def grammar_model_from_file(self, file_name, encoding='utf-8', debug=None,
         return self.metamodel.model_from_file(file_name, debug=debug, **kwargs)
 
 
-# Register built-in textX language. See setup.py entry_points
-textx = LanguageDesc(name='textX',
-                     pattern='*.tx',
-                     description='A meta-language for language definition',
-                     metamodel=lambda: TextXMetaMetaModel())
+# Register built-in textX language. See pyproject.toml entry-points
+textx = LanguageDesc(
+    name="textX",
+    pattern="*.tx",
+    description="A meta-language for language definition",
+    metamodel=lambda: TextXMetaMetaModel(),
+)
diff --git a/textx/model.py b/textx/model.py
index c0230523e..8a8764b1b 100644
--- a/textx/model.py
+++ b/textx/model.py
@@ -2,27 +2,29 @@
 Model construction from parse trees and the model API.
 """
 
-import sys
 import codecs
 import traceback
 from collections import OrderedDict
-from arpeggio import Parser, Sequence, NoMatch, EOF, Terminal
-from textx.exceptions import TextXError, TextXSyntaxError, TextXSemanticError
-from textx.const import MULT_OPTIONAL, MULT_ONE, MULT_ONEORMORE, \
-    MULT_ZEROORMORE, RULE_ABSTRACT, RULE_MATCH, MULT_ASSIGN_ERROR, \
-    UNKNOWN_OBJ_ERROR
+from contextlib import suppress
+
+from arpeggio import EOF, NoMatch, Parser, Sequence, Terminal
+
+from textx.const import (
+    MULT_ASSIGN_ERROR,
+    MULT_ONE,
+    MULT_ONEORMORE,
+    MULT_OPTIONAL,
+    MULT_ZEROORMORE,
+    RULE_ABSTRACT,
+    RULE_MATCH,
+    UNKNOWN_OBJ_ERROR,
+)
+from textx.exceptions import TextXError, TextXSemanticError, TextXSyntaxError
 from textx.lang import PRIMITIVE_PYTHON_TYPES
-from textx.scoping import Postponed, remove_models_from_repositories, \
-    get_included_models
+from textx.scoping import Postponed, get_included_models, remove_models_from_repositories
 from textx.scoping.providers import PlainName as DefaultScopeProvider
 
-if sys.version < '3':
-    text = unicode  # noqa
-else:
-    text = str
-
-__all__ = ['get_children_of_type', 'get_parent_of_type', 'get_model',
-           'get_metamodel']
+__all__ = ["get_children_of_type", "get_parent_of_type", "get_model", "get_metamodel"]
 
 
 def textx_isinstance(obj, obj_cls):
@@ -40,12 +42,15 @@ def textx_isinstance(obj, obj_cls):
         return True
     if isinstance(obj, obj_cls):
         return True
-    if hasattr(obj_cls, "_tx_fqn") and hasattr(obj, "_tx_fqn"):
-        if obj_cls._tx_fqn == obj._tx_fqn:
-            return True
+    if (
+        hasattr(obj_cls, "_tx_fqn")
+        and hasattr(obj, "_tx_fqn")
+        and obj_cls._tx_fqn == obj._tx_fqn
+    ):
+        return True
     if hasattr(obj_cls, "_tx_inh_by"):
         for cls in obj_cls._tx_inh_by:
-            if (textx_isinstance(obj, cls)):
+            if textx_isinstance(obj, cls):
                 return True
     return False
 
@@ -55,7 +60,7 @@ def get_model(obj):
     Finds model root element for the given object.
     """
     p = obj
-    while hasattr(p, 'parent'):
+    while hasattr(p, "parent"):
         p = p.parent
     return p
 
@@ -78,10 +83,10 @@ def get_parent_of_type(typ, obj):
         obj (model object): Python model object which is the start of the
             search process.
     """
-    if type(typ) is not text:
+    if not isinstance(typ, str):
         typ = typ.__name__
 
-    while hasattr(obj, 'parent'):
+    while hasattr(obj, "parent"):
         obj = obj.parent
         if obj.__class__.__name__ == typ:
             return obj
@@ -106,7 +111,6 @@ def get_children(selector, root, children_first=False, should_follow=lambda obj:
     collected_ids = set()
 
     def follow(elem):
-
         if id(elem) in collected_ids:
             # Use id to avoid relying on __eq__ of user class
             return
@@ -114,12 +118,11 @@ def follow(elem):
         # Use meta-model to search for all contained child elements.
         cls = elem.__class__
 
-        if not children_first:
-            if hasattr(cls, '_tx_attrs') and selector(elem):
-                collected.append(elem)
-                collected_ids.add(id(elem))
+        if not children_first and hasattr(cls, "_tx_attrs") and selector(elem):
+            collected.append(elem)
+            collected_ids.add(id(elem))
 
-        if hasattr(cls, '_tx_attrs'):
+        if hasattr(cls, "_tx_attrs"):
             for attr_name, attr in cls._tx_attrs.items():
                 # Follow only attributes with containment semantics
                 if attr.cont:
@@ -134,10 +137,9 @@ def follow(elem):
                                 if should_follow(new_elem):
                                     follow(new_elem)
 
-        if children_first:
-            if hasattr(cls, '_tx_attrs') and selector(elem):
-                collected.append(elem)
-                collected_ids.add(id(elem))
+        if children_first and hasattr(cls, "_tx_attrs") and selector(elem):
+            collected.append(elem)
+            collected_ids.add(id(elem))
 
     follow(root)
     return collected
@@ -160,12 +162,15 @@ def get_children_of_type(typ, root, children_first=False, should_follow=lambda o
             traversed.
     """
 
-    if type(typ) is not text:
+    if not isinstance(typ, str):
         typ = typ.__name__
 
-    return get_children(lambda x: x.__class__.__name__ == typ, root,
-                        children_first=children_first,
-                        should_follow=should_follow)
+    return get_children(
+        lambda x: x.__class__.__name__ == typ,
+        root,
+        children_first=children_first,
+        should_follow=should_follow,
+    )
 
 
 def get_location(model_obj):
@@ -179,9 +184,9 @@ def get_location(model_obj):
         This function may be used to fill exceptions
     """
     from textx.model import get_model
+
     the_model = get_model(model_obj)
-    line, col = the_model._tx_parser.pos_to_linecol(
-        model_obj._tx_position)
+    line, col = the_model._tx_parser.pos_to_linecol(model_obj._tx_position)
     return {"line": line, "col": col, "filename": the_model._tx_filename}
 
 
@@ -206,6 +211,7 @@ def date_converter(model_date):
     Returns:
         the decorated object processor
     """
+
     def wrapper(obj):
         try:
             return obj_processor(obj)
@@ -213,14 +219,15 @@ def wrapper(obj):
             if isinstance(e, TextXError):
                 raise
             else:
-                if hasattr(obj, '_tx_position') and hasattr(obj, '_tx_filename'):
+                if hasattr(obj, "_tx_position") and hasattr(obj, "_tx_filename"):
                     raise TextXError(str(e), **get_location(obj)) from e
                 else:
                     raise TextXError(str(e)) from e
+
     return wrapper
 
 
-class ObjCrossRef(object):
+class ObjCrossRef:
     """
     Used for object cross reference resolving.
 
@@ -235,8 +242,7 @@ class ObjCrossRef(object):
             locally defined scope providers.
     """
 
-    def __init__(self, obj_name, cls, position, scope_provider,
-                 match_rule_name):
+    def __init__(self, obj_name, cls, position, scope_provider, match_rule_name):
         self.obj_name = obj_name
         self.cls = cls
         self.position = position
@@ -244,7 +250,7 @@ def __init__(self, obj_name, cls, position, scope_provider,
         self.match_rule_name = match_rule_name
 
 
-class RefRulePosition(object):
+class RefRulePosition:
     """
     Used for "go to definition" support in textx-languageserver
 
@@ -257,8 +263,9 @@ class RefRulePosition(object):
         def_pos_end(int): Ending position of referenced object
     """
 
-    def __init__(self, name, ref_pos_start, ref_pos_end,
-                 def_file_name, def_pos_start, def_pos_end):
+    def __init__(
+        self, name, ref_pos_start, ref_pos_end, def_file_name, def_pos_start, def_pos_end
+    ):
         self.name = name
         self.ref_pos_start = ref_pos_start
         self.ref_pos_end = ref_pos_end
@@ -280,12 +287,13 @@ class TextXModelParser(Parser):
         """
 
         def __init__(self, *args, **kwargs):
-            super(TextXModelParser, self).__init__(*args, **kwargs)
+            super().__init__(*args, **kwargs)
 
             # By default first rule is starting rule
             # and must be followed by the EOF
             self.parser_model = Sequence(
-                nodes=[top_rule, EOF()], rule_name='Model', root=True)
+                nodes=[top_rule, EOF()], rule_name="Model", root=True
+            )
             self.comments_model = comments_model
 
             # Stack for metaclass instances
@@ -309,6 +317,7 @@ def clone(self):
                 A clone of this parser
             """
             import copy
+
             the_clone = copy.copy(self)  # shallow copy
 
             # create new objects for parse-dependent data
@@ -328,34 +337,51 @@ def _parse(self):
                 return self.parser_model.parse(self)
             except NoMatch as e:
                 e.eval_attrs()
-                raise TextXSyntaxError(message=e.message,
-                                       line=e.line,
-                                       col=e.col,
-                                       filename=e.parser.file_name,
-                                       context=e.context,
-                                       expected_rules=e.rules)
-
-        def get_model_from_file(self, file_name, encoding, debug,
-                                pre_ref_resolution_callback=None,
-                                is_main_model=True):
+                raise TextXSyntaxError(
+                    message=e.message,
+                    line=e.line,
+                    col=e.col,
+                    filename=e.parser.file_name,
+                    context=e.context,
+                    expected_rules=e.rules,
+                ) from e
+
+        def get_model_from_file(
+            self,
+            file_name,
+            encoding,
+            debug,
+            pre_ref_resolution_callback=None,
+            is_main_model=True,
+        ):
             """
             Creates model from the parse tree from the previous parse call.
             If file_name is given file will be parsed before model
             construction.
             """
-            with codecs.open(file_name, 'r', encoding) as f:
+            with codecs.open(file_name, "r", encoding) as f:
                 model_str = f.read()
 
             model = self.get_model_from_str(
-                model_str, file_name=file_name, debug=debug,
+                model_str,
+                file_name=file_name,
+                debug=debug,
                 pre_ref_resolution_callback=pre_ref_resolution_callback,
-                is_main_model=is_main_model, encoding=encoding)
+                is_main_model=is_main_model,
+                encoding=encoding,
+            )
 
             return model
 
-        def get_model_from_str(self, model_str, file_name=None, debug=None,
-                               pre_ref_resolution_callback=None,
-                               is_main_model=True, encoding='utf-8'):
+        def get_model_from_str(
+            self,
+            model_str,
+            file_name=None,
+            debug=None,
+            pre_ref_resolution_callback=None,
+            is_main_model=True,
+            encoding="utf-8",
+        ):
             """
             Parses given string and creates model object graph.
             """
@@ -378,9 +404,13 @@ def get_model_from_str(self, model_str, file_name=None, debug=None,
                 # Transform parse tree to model. Skip root node which
                 # represents the whole file ending in EOF.
                 model = parse_tree_to_objgraph(
-                    self, self.parse_tree[0], file_name=file_name,
+                    self,
+                    self.parse_tree[0],
+                    file_name=file_name,
                     pre_ref_resolution_callback=pre_ref_resolution_callback,
-                    is_main_model=is_main_model, encoding=encoding)
+                    is_main_model=is_main_model,
+                    encoding=encoding,
+                )
 
             except:  # noqa
                 # Restore of user classes replaced attr methods
@@ -388,7 +418,6 @@ def get_model_from_str(self, model_str, file_name=None, debug=None,
                 raise
 
             finally:
-
                 if debug is not None:
                     self.debug = old_debug_state
 
@@ -399,7 +428,7 @@ def _replace_user_attr_methods_for_class(self, user_class):
 
             # Custom attr dunder methods used for user classes during loading
             def _getattribute(obj, name):
-                if name == '__dict__':
+                if name == "__dict__":
                     try:
                         return user_class._tx_obj_attrs[id(obj)]
                     except KeyError:
@@ -430,14 +459,11 @@ def _delattr(obj, name):
                     except (AttributeError, TypeError):
                         return super(user_class, obj).__delattr__(name)
 
-            for a_name in ('setattr', 'delattr',
-                           'getattribute'):
-                real_name = '__{}__'.format(a_name)
-                cached_name = '_tx_real_{}'.format(a_name)
-                setattr(user_class, cached_name,
-                        user_class.__dict__.get(real_name, None))
-                setattr(user_class, real_name,
-                        locals()['_{}'.format(a_name)])
+            for a_name in ("setattr", "delattr", "getattribute"):
+                real_name = f"__{a_name}__"
+                cached_name = f"_tx_real_{a_name}"
+                setattr(user_class, cached_name, user_class.__dict__.get(real_name, None))
+                setattr(user_class, real_name, locals()[f"_{a_name}"])
             user_class._tx_instrumented = 1
 
         def _replace_user_attr_methods(self):
@@ -446,7 +472,7 @@ def _replace_user_attr_methods(self):
             to support postponing of user obj initialization.
             """
             for user_class in self.metamodel.user_classes.values():
-                if '_tx_instrumented' not in user_class.__dict__:
+                if "_tx_instrumented" not in user_class.__dict__:
                     self._replace_user_attr_methods_for_class(user_class)
                 else:
                     user_class._tx_instrumented += 1
@@ -457,17 +483,16 @@ def _restore_user_attr_methods(self):
             classes.
             """
             for user_class in self.metamodel.user_classes.values():
-                if hasattr(user_class, '_tx_instrumented'):
+                if hasattr(user_class, "_tx_instrumented"):
                     user_class._tx_instrumented -= 1
                     if user_class._tx_instrumented == 0:
-                        delattr(user_class, '_tx_instrumented')
-                        for a_name in ('getattr', 'setattr', 'delattr',
-                                       'getattribute'):
-                            cached_name = '_tx_real_{}'.format(a_name)
-                            real_name = '__{}__'.format(a_name)
+                        delattr(user_class, "_tx_instrumented")
+                        for a_name in ("getattr", "setattr", "delattr", "getattribute"):
+                            cached_name = f"_tx_real_{a_name}"
+                            real_name = f"__{a_name}__"
                             if hasattr(user_class, cached_name):
                                 cached_meth = getattr(user_class, cached_name)
-                                if hasattr(cached_meth, 'im_func'):
+                                if hasattr(cached_meth, "im_func"):
                                     # Python 2: func is converted to
                                     # instancemethod
                                     cached_meth = cached_meth.im_func
@@ -480,9 +505,14 @@ def _restore_user_attr_methods(self):
     return TextXModelParser(**kwargs)
 
 
-def parse_tree_to_objgraph(parser, parse_tree, file_name=None,
-                           pre_ref_resolution_callback=None,
-                           is_main_model=True, encoding='utf-8'):
+def parse_tree_to_objgraph(
+    parser,
+    parse_tree,
+    file_name=None,
+    pre_ref_resolution_callback=None,
+    is_main_model=True,
+    encoding="utf-8",
+):
     """
     Transforms parse_tree to object graph representing model in a
     new language.
@@ -500,46 +530,57 @@ def process_match(nt):
         """
         line, col = parser.pos_to_linecol(nt.position)
         if isinstance(nt, Terminal):
-            return metamodel.process(nt.value, nt.rule_name,
-                                     filename=parser.file_name,
-                                     line=line, col=col)
+            return metamodel.process(
+                nt.value, nt.rule_name, filename=parser.file_name, line=line, col=col
+            )
         else:
             # If RHS of assignment is NonTerminal it is a product of
             # complex match rule. Convert nodes to text and do the join.
             if len(nt) > 1:
-                result = "".join([text(process_match(n)) for n in nt])
+                result = "".join([str(process_match(n)) for n in nt])
             else:
                 result = process_match(nt[0])
-            return metamodel.process(result, nt.rule_name,
-                                     filename=parser.file_name,
-                                     line=line, col=col)
+            return metamodel.process(
+                result, nt.rule_name, filename=parser.file_name, line=line, col=col
+            )
 
     def process_node(node):
         line, col = parser.pos_to_linecol(node.position)
         if isinstance(node, Terminal):
             from arpeggio import RegExMatch
-            if metamodel.use_regexp_group and \
-                    isinstance(node.rule, RegExMatch):
+
+            if metamodel.use_regexp_group and isinstance(node.rule, RegExMatch):
                 if node.rule.regex.groups == 1:
                     value = node.extra_info.group(1)
-                    return metamodel.process(value, node.rule_name,
-                                             filename=parser.file_name,
-                                             line=line, col=col)
+                    return metamodel.process(
+                        value,
+                        node.rule_name,
+                        filename=parser.file_name,
+                        line=line,
+                        col=col,
+                    )
                 else:
-                    return metamodel.process(node.value, node.rule_name,
-                                             filename=parser.file_name,
-                                             line=line, col=col)
+                    return metamodel.process(
+                        node.value,
+                        node.rule_name,
+                        filename=parser.file_name,
+                        line=line,
+                        col=col,
+                    )
             else:
-                return metamodel.process(node.value, node.rule_name,
-                                         filename=parser.file_name,
-                                         line=line, col=col)
-
-        assert node.rule.root, \
-            "Not a root node: {}".format(node.rule.rule_name)
+                return metamodel.process(
+                    node.value,
+                    node.rule_name,
+                    filename=parser.file_name,
+                    line=line,
+                    col=col,
+                )
+
+        assert node.rule.root, f"Not a root node: {node.rule.rule_name}"
         # If this node is created by some root rule
         # create metaclass instance.
         inst = None
-        if not node.rule_name.startswith('__asgn'):
+        if not node.rule_name.startswith("__asgn"):
             # If not assignment
             # Get class
             mclass = node.rule._tx_class
@@ -551,12 +592,16 @@ def process_node(node):
                 if len(node) > 1:
                     try:
                         return process_node(
-                            next(n for n in node
-                                 if type(n) is not Terminal
-                                 and n.rule._tx_class is not RULE_MATCH))  # noqa
+                            next(
+                                n
+                                for n in node
+                                if type(n) is not Terminal
+                                and n.rule._tx_class is not RULE_MATCH
+                            )
+                        )  # noqa
                     except StopIteration:
                         # All nodes are match rules, do concatenation
-                        return ''.join(text(n) for n in node)
+                        return "".join(str(n) for n in node)
                 else:
                     return process_node(node[0])
             elif mclass._tx_type == RULE_MATCH:
@@ -565,7 +610,7 @@ def process_node(node):
                 return process_match(node)
 
             if parser.debug:
-                parser.dprint("CREATING INSTANCE {}".format(node.rule_name))
+                parser.dprint(f"CREATING INSTANCE {node.rule_name}")
 
             # If user class is given
             # use it instead of generic one
@@ -599,8 +644,7 @@ def process_node(node):
 
             for n in node:
                 if parser.debug:
-                    parser.dprint("Recursing into {} = '{}'"
-                                  .format(type(n).__name__, text(n)))
+                    parser.dprint(f"Recursing into {type(n).__name__} = '{n}'")
                 process_node(n)
 
             parser._inst_stack.pop()
@@ -610,49 +654,51 @@ def process_node(node):
 
             # If this object is nested add 'parent' reference
             if parser._inst_stack:
-                setattr(obj_attrs, 'parent', parser._inst_stack[-1][0])
+                obj_attrs.parent = parser._inst_stack[-1][0]
 
             # Special case for 'name' attrib. It is used for cross-referencing
-            if hasattr(inst, 'name') and inst.name:
+            if hasattr(inst, "name") and inst.name:
                 # Objects of each class are in its own namespace
-                if not id(inst.__class__) in parser._instances:
+                if id(inst.__class__) not in parser._instances:
                     parser._instances[id(inst.__class__)] = {}
                 try:
                     parser._instances[id(inst.__class__)][inst.name] = inst
                 except TypeError as e:
-                    if 'unhashable type' in e.args[0]:
+                    if "unhashable type" in e.args[0]:
                         raise TextXSemanticError(
-                            'Object name can\'t be of unhashable type.'
-                            ' Please see the note in this docs'
-                            ' section http://textx.github.io/textX/stable/grammar/#references')  # noqa
+                            "Object name can't be of unhashable type."
+                            " Please see the note in this docs"
+                            " section http://textx.github.io/textX/stable/grammar/#references"
+                        ) from e
                     raise
 
             if parser.debug:
-                parser.dprint("LEAVING INSTANCE {}".format(node.rule_name))
+                parser.dprint(f"LEAVING INSTANCE {node.rule_name}")
 
         else:
             # Handle assignments
             attr_name = node.rule._attr_name
-            op = node.rule_name.split('_')[-1]
+            op = node.rule_name.split("_")[-1]
             model_obj, obj_attr = parser._inst_stack[-1]
             cls = type(model_obj)
             metaattr = cls._tx_attrs[attr_name]
 
             if parser.debug:
-                parser.dprint('Handling assignment: {} {}...'
-                              .format(op, attr_name))
+                parser.dprint(f"Handling assignment: {op} {attr_name}...")
 
-            if op == 'optional':
+            if op == "optional":
                 setattr(obj_attr, attr_name, True)
 
-            elif op == 'plain':
+            elif op == "plain":
                 attr_value = getattr(obj_attr, attr_name)
-                if attr_value and type(attr_value) is not list:
+                if attr_value and not isinstance(attr_value, list):
                     fmt = "Multiple assignments to attribute {} at {}"
                     raise TextXSemanticError(
                         message=fmt.format(
-                            attr_name, parser.pos_to_linecol(node.position)),
-                        err_type=MULT_ASSIGN_ERROR)
+                            attr_name, parser.pos_to_linecol(node.position)
+                        ),
+                        err_type=MULT_ASSIGN_ERROR,
+                    )
 
                 # Convert tree bellow assignment to proper value
                 value = process_node(node[0])
@@ -661,22 +707,25 @@ def process_node(node):
                     # If this is non-containing reference create ObjCrossRef
                     p = metaattr.scope_provider
                     rn = metaattr.match_rule_name
-                    value = ObjCrossRef(obj_name=value, cls=metaattr.cls,
-                                        position=node[0].position,
-                                        scope_provider=p,
-                                        match_rule_name=rn)
+                    value = ObjCrossRef(
+                        obj_name=value,
+                        cls=metaattr.cls,
+                        position=node[0].position,
+                        scope_provider=p,
+                        match_rule_name=rn,
+                    )
                     parser._crossrefs.append((model_obj, metaattr, value))
                     return model_obj
 
-                if type(attr_value) is list:
+                if isinstance(attr_value, list):
                     attr_value.append(value)
                 else:
                     setattr(obj_attr, attr_name, value)
 
-            elif op in ['list', 'oneormore', 'zeroormore']:
+            elif op in ["list", "oneormore", "zeroormore"]:
                 for n in node:
                     # If the node is separator skip
-                    if n.rule_name != 'sep':
+                    if n.rule_name != "sep":
                         # Convert node to proper type
                         # Rule links will be resolved later
                         value = process_node(n)
@@ -687,23 +736,26 @@ def process_node(node):
 
                             p = metaattr.scope_provider
                             rn = metaattr.match_rule_name
-                            value = ObjCrossRef(obj_name=value,
-                                                cls=metaattr.cls,
-                                                position=n.position,
-                                                scope_provider=p,
-                                                match_rule_name=rn)
-
-                            parser._crossrefs.append((obj_attr, metaattr,
-                                                      value))
+                            value = ObjCrossRef(
+                                obj_name=value,
+                                cls=metaattr.cls,
+                                position=n.position,
+                                scope_provider=p,
+                                match_rule_name=rn,
+                            )
+
+                            parser._crossrefs.append((obj_attr, metaattr, value))
                             continue
 
-                        if not hasattr(obj_attr, attr_name) or \
-                                getattr(obj_attr, attr_name) is None:
+                        if (
+                            not hasattr(obj_attr, attr_name)
+                            or getattr(obj_attr, attr_name) is None
+                        ):
                             setattr(obj_attr, attr_name, [])
                         getattr(obj_attr, attr_name).append(value)
             else:
                 # This shouldn't happen
-                assert False
+                raise AssertionError()
 
         # Collect rules for textx-tools
         if inst is not None and metamodel.textx_tools_support:
@@ -712,19 +764,17 @@ def process_node(node):
 
         return inst
 
-    def call_obj_processors(metamodel, model_obj,
-                            metaclass_of_grammar_rule=None):
+    def call_obj_processors(metamodel, model_obj, metaclass_of_grammar_rule=None):
         """
         Depth-first model object processing.
         """
         try:
             if metaclass_of_grammar_rule is None:
-                metaclass_of_grammar_rule = \
-                    metamodel[model_obj.__class__.__name__]
-        except KeyError:
+                metaclass_of_grammar_rule = metamodel[model_obj.__class__.__name__]
+        except KeyError as e:
             raise TextXSemanticError(
-                'Unknown meta-class "{}".'
-                .format(model.obj.__class__.__name__))
+                f'Unknown meta-class "{model.obj.__class__.__name__}".'
+            ) from e
 
         if metaclass_of_grammar_rule._tx_type is RULE_MATCH:
             # Object processors for match rules are already called
@@ -740,7 +790,7 @@ def call_obj_processors(metamodel, model_obj,
         # enter recursive visit of attributes only, if the class of the
         # object being processed is a meta class of the current meta model
         if model_obj.__class__.__name__ in metamodel:
-            if hasattr(model_obj, '_tx_fqn'):
+            if hasattr(model_obj, "_tx_fqn"):
                 current_metaclass_of_obj = metamodel[model_obj._tx_fqn]
             else:
                 # fallback (not used - unsure if this case is required...):
@@ -755,32 +805,34 @@ def call_obj_processors(metamodel, model_obj,
                         if metaattr.mult in many:
                             for idx, obj in enumerate(attr):
                                 if obj is not None:
-                                    result = call_obj_processors(metamodel,
-                                                                 obj,
-                                                                 metaattr.cls)
+                                    result = call_obj_processors(
+                                        metamodel, obj, metaattr.cls
+                                    )
                                     if result is not None:
                                         attr[idx] = result
                         else:
-                            result = call_obj_processors(metamodel,
-                                                         attr, metaattr.cls)
+                            result = call_obj_processors(metamodel, attr, metaattr.cls)
                             if result is not None:
                                 setattr(model_obj, metaattr.name, result)
 
-            if current_metaclass_of_obj._tx_fqn !=\
-                    metaclass_of_grammar_rule._tx_fqn:
+            if (
+                current_metaclass_of_obj._tx_fqn != metaclass_of_grammar_rule._tx_fqn
+                and metamodel.has_obj_processor(current_metaclass_of_obj.__name__)
+            ):
                 # This can happen if grammar rule is abstract or if model is
                 # modified (e.g. expression reduction)
-                if metamodel.has_obj_processor(current_metaclass_of_obj.__name__):
-                    return_value_current = metamodel.process(
-                        model_obj, current_metaclass_of_obj.__name__,
-                        **get_location(model_obj))
+                return_value_current = metamodel.process(
+                    model_obj,
+                    current_metaclass_of_obj.__name__,
+                    **get_location(model_obj),
+                )
 
         # call obj_proc of rule found in grammar
         if metamodel.has_obj_processor(metaclass_of_grammar_rule.__name__):
             loc = get_location(model_obj)
-            return_value_grammar = metamodel.process(model_obj,
-                                                     metaclass_of_grammar_rule.__name__,
-                                                     **loc)
+            return_value_grammar = metamodel.process(
+                model_obj, metaclass_of_grammar_rule.__name__, **loc
+            )
 
         # both obj_processors are called, if two different processors
         # are defined for the object metaclass and the grammar metaclass
@@ -829,14 +881,15 @@ def call_obj_processors(metamodel, model_obj,
         if pre_ref_resolution_callback:
             pre_ref_resolution_callback(model)
 
-        if hasattr(model, '_tx_metamodel'):
-            assert hasattr(model, '_tx_model_params')
+        if hasattr(model, "_tx_metamodel"):
+            assert hasattr(model, "_tx_model_params")
 
         # Load all imported models (e.g. using importURI)
         # based on the maker `ModelLoader` found in the
         # defined scope providers:
         for scope_provider in metamodel.scope_providers.values():
             from textx.scoping import ModelLoader
+
             if isinstance(scope_provider, ModelLoader):
                 scope_provider.load_models(model, encoding=encoding)
 
@@ -847,21 +900,24 @@ def call_obj_processors(metamodel, model_obj,
             crossref = crossref[2]
             if crossref.scope_provider is not None:
                 from textx.scoping import ModelLoader
+
                 scope_provider = crossref.scope_provider
                 if isinstance(scope_provider, ModelLoader):
                     scope_provider.load_models(model, encoding=encoding)
 
         if not is_immutable_obj:
             model._tx_reference_resolver = ReferenceResolver(
-                parser, model, pos_crossref_list)
+                parser, model, pos_crossref_list
+            )
             model._tx_parser = parser
 
         if is_main_model:
             models = get_included_models(model)
             try:
                 # filter out all models w/o resolver:
-                models = list(filter(
-                    lambda x: hasattr(x, "_tx_reference_resolver"), models))
+                models = list(
+                    filter(lambda x: hasattr(x, "_tx_reference_resolver"), models)
+                )
 
                 resolved_count = 1
                 unresolved_count = 1
@@ -870,22 +926,23 @@ def call_obj_processors(metamodel, model_obj,
                     unresolved_count = 0
                     # print("***RESOLVING {} models".format(len(models)))
                     for m in models:
-                        resolved_count_for_this_model, delayed_crossrefs = \
-                            m._tx_reference_resolver.resolve_one_step()
+                        (
+                            resolved_count_for_this_model,
+                            delayed_crossrefs,
+                        ) = m._tx_reference_resolver.resolve_one_step()
                         resolved_count += resolved_count_for_this_model
                         unresolved_count += len(delayed_crossrefs)
                     # print("DEBUG: delayed #:{} unresolved #:{}".
                     #      format(unresolved_count,unresolved_count))
-                if (unresolved_count > 0):
+                if unresolved_count > 0:
                     error_text = "Unresolvable cross references:"
 
                     for m in models:
-                        for _, _, delayed \
-                                in m._tx_reference_resolver.delayed_crossrefs:
+                        for _, _, delayed in m._tx_reference_resolver.delayed_crossrefs:
                             line, col = parser.pos_to_linecol(delayed.position)
                             error_text += ' "{}" of class "{}" at {}'.format(
-                                delayed.obj_name, delayed.cls.__name__, (
-                                    line, col))
+                                delayed.obj_name, delayed.cls.__name__, (line, col)
+                            )
                     raise TextXSemanticError(error_text, line=line, col=col)
 
                 for m in models:
@@ -897,8 +954,7 @@ def call_obj_processors(metamodel, model_obj,
 
                 # final check that everything went ok
                 for m in models:
-                    assert 0 == len(get_children_of_type(
-                        Postponed.__class__, m))
+                    assert len(get_children_of_type(Postponed.__class__, m)) == 0
 
                     # We have model loaded and all link resolved
                     # So we shall do a depth-first call of object
@@ -914,8 +970,7 @@ def call_obj_processors(metamodel, model_obj,
                 remove_models_from_repositories(models, models)
                 raise
 
-        if metamodel.textx_tools_support \
-                and type(model) not in PRIMITIVE_PYTHON_TYPES:
+        if metamodel.textx_tools_support and type(model) not in PRIMITIVE_PYTHON_TYPES:
             # Cross-references for go-to definition language server support
             # Already sorted based on ref_pos_start attr
             # (required for binary search)
@@ -923,9 +978,9 @@ def call_obj_processors(metamodel, model_obj,
 
             # Dict for storing rules where key is position of rule instance in
             # text. Sorted based on nested rules.
-            model._pos_rule_dict = OrderedDict(sorted(pos_rule_dict.items(),
-                                                      key=lambda x: x[0],
-                                                      reverse=True))
+            model._pos_rule_dict = OrderedDict(
+                sorted(pos_rule_dict.items(), key=lambda x: x[0], reverse=True)
+            )
     # exception occurred during model creation
     except:  # noqa
         _remove_all_affected_models_in_construction(model)
@@ -954,7 +1009,7 @@ def _end_model_construction(model):
     # At this point we should restore original user attr methods as
     # attribute collection is over and we should switch back to
     # "normal" behavior
-    if hasattr(model, '_tx_parser'):  # not for, e.g., str
+    if hasattr(model, "_tx_parser"):  # not for, e.g., str
         the_parser = model._tx_parser
         the_parser._restore_user_attr_methods()
 
@@ -970,17 +1025,17 @@ def _end_model_construction(model):
                 # First try to apply attributes directly. It might
                 # not be possible for some (e.g. __slots__ are used)
                 for name, value in attrs.items():
-                    try:
-                        setattr(obj, name, value)
-                    except:  # noqa
+                    with suppress(Exception):
                         # Not possible to set the attribute
-                        pass
+                        setattr(obj, name, value)
 
                 # We shall only pass to __init__ attributes that are
                 # defined by the meta-model, and `parent` if applicable
-                attrs = {k: v for k, v in attrs.items()
-                         if k in obj.__class__._tx_attrs
-                         or k == 'parent'}
+                attrs = {
+                    k: v
+                    for k, v in attrs.items()
+                    if k in obj.__class__._tx_attrs or k == "parent"
+                }
 
                 # Call constructor for custom initialization
                 obj.__init__(**attrs)
@@ -988,8 +1043,7 @@ def _end_model_construction(model):
             except TypeError as e:
                 # Add class name information in case of wrong
                 # constructor parameters
-                e.args += ("for class %s" %
-                           obj.__class__.__name__,)
+                e.args += ("for class %s" % obj.__class__.__name__,)
                 the_parser.dprint(traceback.print_exc())
                 raise e
 
@@ -1004,11 +1058,10 @@ def _remove_all_affected_models_in_construction(model):
     See: _start_model_construction
     """
     all_affected_models = get_included_models(model)
-    models_to_be_removed = list(filter(
-        lambda x: hasattr(x, "_tx_reference_resolver"),
-        all_affected_models))
-    remove_models_from_repositories(all_affected_models,
-                                    models_to_be_removed)
+    models_to_be_removed = list(
+        filter(lambda x: hasattr(x, "_tx_reference_resolver"), all_affected_models)
+    )
+    remove_models_from_repositories(all_affected_models, models_to_be_removed)
 
 
 class ReferenceResolver:
@@ -1033,13 +1086,11 @@ def has_unresolved_crossrefs(self, obj, attr_name=None):
             True (has unresolved crossrefs) or False (else)
         """
         if get_model(obj) != self.model:
-            return get_model(obj). \
-                _tx_reference_resolver.has_unresolved_crossrefs(obj)
+            return get_model(obj)._tx_reference_resolver.has_unresolved_crossrefs(obj)
         else:
-            for crossref_obj, attr, crossref in self.parser._crossrefs:
-                if crossref_obj is obj:
-                    if (not attr_name) or attr_name == attr.name:
-                        return True
+            for crossref_obj, attr, _ in self.parser._crossrefs:
+                if crossref_obj is obj and ((not attr_name) or attr_name == attr.name):
+                    return True
             return False
 
     def resolve_one_step(self):
@@ -1060,57 +1111,70 @@ def resolve_one_step(self):
         # -------------------------
         default_scope = DefaultScopeProvider()
         for obj, attr, crossref in current_crossrefs:
-            if (get_model(obj) == self.model):
+            if get_model(obj) == self.model:
                 attr_value = getattr(obj, attr.name)
-                attr_refs = [obj.__class__.__name__ + "." + attr.name,
-                             "*." + attr.name, obj.__class__.__name__ + ".*",
-                             "*.*"]
+                attr_refs = [
+                    obj.__class__.__name__ + "." + attr.name,
+                    "*." + attr.name,
+                    obj.__class__.__name__ + ".*",
+                    "*.*",
+                ]
                 if crossref.scope_provider is not None:
                     resolved = crossref.scope_provider(obj, attr, crossref)
                 else:
                     for attr_ref in attr_refs:
                         if attr_ref in metamodel.scope_providers:
                             if self.parser.debug:
-                                self.parser.dprint(" FOUND {}".format(attr_ref))
+                                self.parser.dprint(f" FOUND {attr_ref}")
                             resolved = metamodel.scope_providers[attr_ref](
-                                obj, attr, crossref)
+                                obj, attr, crossref
+                            )
                             break
                     else:
                         resolved = default_scope(obj, attr, crossref)
 
                 # Collect cross-references for textx-tools
-                if resolved is not None and not type(resolved) is Postponed:
-                    if metamodel.textx_tools_support:
-                        self.pos_crossref_list.append(
-                            RefRulePosition(
-                                name=crossref.obj_name,
-                                ref_pos_start=crossref.position,
-                                ref_pos_end=crossref.position + len(
-                                    resolved.name),
-                                def_file_name=get_model(resolved)._tx_filename,
-                                def_pos_start=resolved._tx_position,
-                                def_pos_end=resolved._tx_position_end))
-
-                if resolved is None:
-                    # As a fall-back search builtins if given
-                    if metamodel.builtins:
-                        if crossref.obj_name in metamodel.builtins:
-                            from textx import textx_isinstance
-                            if textx_isinstance(
-                                    metamodel.builtins[crossref.obj_name],
-                                    crossref.cls
-                            ):
-                                resolved = metamodel.builtins[
-                                    crossref.obj_name]
+                if (
+                    resolved is not None
+                    and type(resolved) is not Postponed
+                    and metamodel.textx_tools_support
+                ):
+                    self.pos_crossref_list.append(
+                        RefRulePosition(
+                            name=crossref.obj_name,
+                            ref_pos_start=crossref.position,
+                            ref_pos_end=crossref.position + len(resolved.name),
+                            def_file_name=get_model(resolved)._tx_filename,
+                            def_pos_start=resolved._tx_position,
+                            def_pos_end=resolved._tx_position_end,
+                        )
+                    )
+
+                # As a fall-back search builtins if given
+                if (
+                    resolved is None
+                    and metamodel.builtins
+                    and crossref.obj_name in metamodel.builtins
+                ):
+                    from textx import textx_isinstance
+
+                    if textx_isinstance(
+                        metamodel.builtins[crossref.obj_name], crossref.cls
+                    ):
+                        resolved = metamodel.builtins[crossref.obj_name]
 
                 if resolved is None:
                     line, col = self.parser.pos_to_linecol(crossref.position)
                     raise TextXSemanticError(
                         message='Unknown object "{}" of class "{}"'.format(
-                            crossref.obj_name, crossref.cls.__name__),
-                        line=line, col=col, err_type=UNKNOWN_OBJ_ERROR,
+                            crossref.obj_name, crossref.cls.__name__
+                        ),
+                        line=line,
+                        col=col,
+                        err_type=UNKNOWN_OBJ_ERROR,
                         expected_obj_cls=crossref.cls,
-                        filename=self.model._tx_filename)
+                        filename=self.model._tx_filename,
+                    )
 
                 if type(resolved) is Postponed:
                     self.delayed_crossrefs.append((obj, attr, crossref))
diff --git a/textx/model_params.py b/textx/model_params.py
index 93e059441..817946621 100644
--- a/textx/model_params.py
+++ b/textx/model_params.py
@@ -2,15 +2,11 @@
 Management of parameters passed to model_from_str or model_from_file.
 """
 
-import sys
-from functools import reduce
 from collections import namedtuple
-from textx.exceptions import TextXError
+from collections.abc import Mapping
+from functools import reduce
 
-if sys.version < '3':
-    from collections import Mapping
-else:
-    from collections.abc import Mapping
+from textx.exceptions import TextXError
 
 
 class ModelParams(Mapping):
@@ -43,9 +39,7 @@ def __keytransform__(self, key):
         return key
 
     def _have_all_parameters_been_used(self):
-        return reduce(
-            lambda r, k: r and (k in self.used_keys),
-            self.store.keys(), True)
+        return reduce(lambda r, k: r and (k in self.used_keys), self.store.keys(), True)
 
     @property
     def all_used(self):
@@ -56,9 +50,7 @@ def all_used(self):
 """
 Class describing a model parameter.
 """
-ModelParamDefinition = namedtuple(
-    'ModelKwargDefinition',
-    ['name', 'description'])
+ModelParamDefinition = namedtuple("ModelKwargDefinition", ["name", "description"])
 
 
 class ModelParamDefinitions(Mapping):
@@ -75,6 +67,7 @@ class ModelParamDefinitions(Mapping):
     case the "outer" metamodel is responsible to restrict
     the possible parameters.
     """
+
     def __init__(self):
         self.store = dict()
 
@@ -94,6 +87,6 @@ def add(self, name, description):
         self.store[name] = ModelParamDefinition(name, description)
 
     def check_params(self, source, **kwargs):
-        for k in kwargs.keys():
-            if k not in self.store.keys():
-                raise TextXError("unknown parameter {} ({})".format(k, source))
+        for k in kwargs:
+            if k not in self.store:
+                raise TextXError(f"unknown parameter {k} ({source})")
diff --git a/textx/registration.py b/textx/registration.py
index 3beb8a7f6..5506fe0c4 100644
--- a/textx/registration.py
+++ b/textx/registration.py
@@ -1,14 +1,18 @@
 """
 Languages and generators registration and discovery API.
 """
-from __future__ import unicode_literals
 import fnmatch
-import pkg_resources
+import sys
+
+if sys.version_info < (3, 10):
+    from importlib_metadata import entry_points
+else:
+    from importlib.metadata import entry_points
 
 from textx.exceptions import TextXRegistrationError
 
 
-class LanguageDesc(object):
+class LanguageDesc:
     """
     A class used in language registration/discovery.
 
@@ -20,14 +24,14 @@ class LanguageDesc(object):
             or the metamodel itself (if a single specific instance is
             desired)
         project_name (str): Read-only attribute available on registrations from
-            `setup.py`. Keeps the Python project name of the project that
+            `pyproject.toml`. Keeps the Python project name of the project that
             registered this language.
         project_version (str): Read-only attribute available on registrations
-            from `setup.py`. Keeps the Python project name of the project that
+            from `pyproject.toml`. Keeps the Python project name of the project that
             registered this language.
     """
 
-    def __init__(self, name, pattern=None, description='', metamodel=None):
+    def __init__(self, name, pattern=None, description="", metamodel=None):
         self.name = name
         self.pattern = pattern
         self.description = description
@@ -36,7 +40,7 @@ def __init__(self, name, pattern=None, description='', metamodel=None):
         self.project_version = None
 
 
-class GeneratorDesc(object):
+class GeneratorDesc:
     """
     A class used in generators registration/discovery.
 
@@ -50,13 +54,14 @@ class GeneratorDesc(object):
                               def generator(metamodel, model, output_path,
                                             overwrite, debug, **custom_args)
         project_name (str): Read-only attribute available on registrations from
-            `setup.py`. Keeps the Python project name of the project that
+            `pyproject.toml`. Keeps the Python project name of the project that
             registered this generator.
         project_version (str): Read-only attribute available on registrations
-            from `setup.py`. Keeps the Python project name of the project that
+            from `pyproject.toml`. Keeps the Python project name of the project that
             registered this language.
     """
-    def __init__(self, language, target, description='', generator=None):
+
+    def __init__(self, language, target, description="", generator=None):
         self.language = language
         self.target = target
         self.description = description
@@ -77,11 +82,10 @@ def language_descriptions():
     global languages
     if languages is None:
         languages = {}
-        for language in pkg_resources.WorkingSet().iter_entry_points(
-                group='textx_languages'):
-            register_language_with_project(language.load(),
-                                           language.dist.project_name,
-                                           language.dist.version)
+        for language in entry_points(group="textx_languages"):
+            register_language_with_project(
+                language.load(), language.dist.name, language.dist.version
+            )
     return languages
 
 
@@ -92,11 +96,10 @@ def generator_descriptions():
     global generators
     if generators is None:
         generators = {}
-        for generator in pkg_resources.WorkingSet().iter_entry_points(
-                group='textx_generators'):
-            register_generator_with_project(generator.load(),
-                                            generator.dist.project_name,
-                                            generator.dist.version)
+        for generator in entry_points(group="textx_generators"):
+            register_generator_with_project(
+                generator.load(), generator.dist.name, generator.dist.version
+            )
     return generators
 
 
@@ -110,9 +113,8 @@ def language_description(language_name):
         language_descriptions()
     try:
         return languages[language_name]
-    except KeyError:
-        raise TextXRegistrationError('Language "{}" not registered.'
-                                     .format(language_name))
+    except KeyError as e:
+        raise TextXRegistrationError(f'Language "{language_name}" not registered.') from e
 
 
 def generator_description(language_name, target_name, any_permitted=False):
@@ -133,26 +135,28 @@ def generator_description(language_name, target_name, any_permitted=False):
         except KeyError:
             if not any_permitted:
                 raise
-            generators_for_language = generators['any']
+            generators_for_language = generators["any"]
             return generators_for_language[target_name]
-    except KeyError:
+    except KeyError as e:
         raise TextXRegistrationError(
-            'No generators registered for language "{}" and target "{}".'
-            .format(language_name, target_name))
+            "No generators registered for language "
+            f'"{language_name}" and target "{target_name}".'
+        ) from e
 
 
-def generator_for_language_target(language_name, target_name,
-                                  any_permitted=False):
+def generator_for_language_target(language_name, target_name, any_permitted=False):
     """
     Return generator callable for the given language name and target name.
     """
-    generator_desc = generator_description(language_name, target_name,
-                                           any_permitted=any_permitted)
+    generator_desc = generator_description(
+        language_name, target_name, any_permitted=any_permitted
+    )
     return generator_desc.generator
 
 
-def register_language(language_desc_or_name, pattern=None, description='',
-                      metamodel=None):
+def register_language(
+    language_desc_or_name, pattern=None, description="", metamodel=None
+):
     """
     Programmatically register a language.
 
@@ -170,19 +174,19 @@ def register_language(language_desc_or_name, pattern=None, description='',
             name=language_desc_or_name,
             pattern=pattern,
             description=description,
-            metamodel=metamodel
+            metamodel=metamodel,
         )
     else:
         language_desc = language_desc_or_name
 
     if language_desc.name.lower() in languages:
         raise TextXRegistrationError(
-            'Language "{}" already registered.'.format(language_desc.name))
+            f'Language "{language_desc.name}" already registered.'
+        )
     languages[language_desc.name.lower()] = language_desc
 
 
-def register_language_with_project(language_desc, project_name,
-                                   project_version):
+def register_language_with_project(language_desc, project_name, project_version):
     """
     Register language with Python project name.
     """
@@ -200,8 +204,9 @@ def clear_language_registrations():
     metamodels = {}
 
 
-def register_generator(generator_desc_or_language, target=None, description='',
-                       generator=None):
+def register_generator(
+    generator_desc_or_language, target=None, description="", generator=None
+):
     """
     Programmatically register a generator.
 
@@ -219,7 +224,7 @@ def register_generator(generator_desc_or_language, target=None, description='',
             language=generator_desc_or_language,
             target=target,
             description=description,
-            generator=generator
+            generator=generator,
         )
     else:
         generator_desc = generator_desc_or_language
@@ -228,12 +233,13 @@ def register_generator(generator_desc_or_language, target=None, description='',
     if generator_desc.target.lower() in lang_gens:
         raise TextXRegistrationError(
             'Generator "{}->{}" already registered.'.format(
-                generator_desc.language, generator_desc.target))
+                generator_desc.language, generator_desc.target
+            )
+        )
     lang_gens[generator_desc.target.lower()] = generator_desc
 
 
-def register_generator_with_project(generator_desc, project_name,
-                                    project_version):
+def register_generator_with_project(generator_desc, project_name, project_version):
     """
     Register generator with Python project name.
     """
@@ -257,18 +263,18 @@ def metamodel_for_language(language_name, **kwargs):
     """
     language_name = language_name.lower()
     if language_name not in metamodels or kwargs:
-        from textx.metamodel import TextXMetaModel, TextXMetaMetaModel
+        from textx.metamodel import TextXMetaMetaModel, TextXMetaModel
+
         language = language_description(language_name)
-        if (isinstance(language.metamodel, TextXMetaModel)
-                or isinstance(language.metamodel, TextXMetaMetaModel)):
+        if isinstance(language.metamodel, (TextXMetaModel, TextXMetaMetaModel)):
             metamodels[language_name] = language.metamodel
         else:
             metamodel = language.metamodel(**kwargs)
-            if not (isinstance(metamodel, TextXMetaModel)
-                    or isinstance(metamodel, TextXMetaMetaModel)):
+            if not (isinstance(metamodel, (TextXMetaModel, TextXMetaMetaModel))):
                 raise TextXRegistrationError(
-                    'Meta-model type for language "{}" is "{}".'
-                    .format(language_name, type(metamodel).__name__))
+                    "Meta-model type for language "
+                    f'"{language_name}" is "{metamodel.__class__.__name__}".'
+                )
             metamodels[language_name] = metamodel
     return metamodels[language_name]
 
@@ -279,8 +285,9 @@ def languages_for_file(file_name_or_pattern):
     """
     file_languages = []
     for language in language_descriptions().values():
-        if file_name_or_pattern == language.pattern \
-                or fnmatch.fnmatch(file_name_or_pattern, language.pattern):
+        if file_name_or_pattern == language.pattern or fnmatch.fnmatch(
+            file_name_or_pattern, language.pattern
+        ):
             file_languages.append(language)
     return file_languages
 
@@ -292,11 +299,13 @@ def language_for_file(file_name_or_pattern):
     """
     languages = languages_for_file(file_name_or_pattern)
     if len(languages) > 1:
-        raise TextXRegistrationError('Multiple languages can parse "{}".'
-                                     .format(file_name_or_pattern))
+        raise TextXRegistrationError(
+            f'Multiple languages can parse "{file_name_or_pattern}".'
+        )
     elif len(languages) == 0:
-        raise TextXRegistrationError('No language registered that can parse'
-                                     ' "{}".'.format(file_name_or_pattern))
+        raise TextXRegistrationError(
+            "No language registered that can parse" f' "{file_name_or_pattern}".'
+        )
 
     return languages[0]
 
@@ -305,8 +314,10 @@ def metamodels_for_file(file_name_or_pattern):
     """
     Return meta-models that can parse the given file .
     """
-    return [metamodel_for_language(language.name)
-            for language in languages_for_file(file_name_or_pattern)]
+    return [
+        metamodel_for_language(language.name)
+        for language in languages_for_file(file_name_or_pattern)
+    ]
 
 
 def metamodel_for_file(file_name_or_pattern, **kwargs):
@@ -314,8 +325,7 @@ def metamodel_for_file(file_name_or_pattern, **kwargs):
     Return a meta-model that can parse the given file or raise
     `TextXRegistrationError` if more than one is registered.
     """
-    return metamodel_for_language(language_for_file(file_name_or_pattern).name,
-                                  **kwargs)
+    return metamodel_for_language(language_for_file(file_name_or_pattern).name, **kwargs)
 
 
 def generator(language, target):
@@ -330,8 +340,10 @@ def _generator(gen_f):
         return GeneratorDesc(
             language=language,
             target=target,
-            description=gen_f.__doc__ if gen_f.__doc__ is not None else '',
-            generator=gen_f)
+            description=gen_f.__doc__ if gen_f.__doc__ is not None else "",
+            generator=gen_f,
+        )
+
     return _generator
 
 
@@ -347,7 +359,8 @@ def language(gen_f):
         return LanguageDesc(
             name=name,
             pattern=pattern,
-            description=gen_f.__doc__.strip()
-            if gen_f.__doc__ is not None else '',
-            metamodel=gen_f)
+            description=gen_f.__doc__.strip() if gen_f.__doc__ is not None else "",
+            metamodel=gen_f,
+        )
+
     return language
diff --git a/textx/scoping/__init__.py b/textx/scoping/__init__.py
index f037855d5..1843a01ab 100644
--- a/textx/scoping/__init__.py
+++ b/textx/scoping/__init__.py
@@ -5,15 +5,16 @@
 # License: MIT License
 #######################################################################
 
+import errno
 import glob
 import os
-import errno
-from os.path import join, exists, abspath
+from os.path import abspath, exists, join
 
 
 def metamodel_for_file_or_default_metamodel(filename, the_metamodel):
     from textx import metamodel_for_file
     from textx.exceptions import TextXRegistrationError
+
     try:
         return metamodel_for_file(filename)
     except TextXRegistrationError:
@@ -24,7 +25,8 @@ def metamodel_for_file_or_default_metamodel(filename, the_metamodel):
 # Scope helper classes:
 # -----------------------------------------------------------------------------
 
-class Postponed(object):
+
+class Postponed:
     """
     Return an object of this class to postpone a reference resolution.
     If you get circular dependencies in resolution logic, an error
@@ -32,7 +34,7 @@ class Postponed(object):
     """
 
 
-class ModelRepository(object):
+class ModelRepository:
     """
     This class has the responsibility to hold a set of (model-identifiers,
     model) pairs as dictionary.
@@ -51,7 +53,7 @@ def add_model(self, model):
         if model._tx_filename:
             filename = abspath(model._tx_filename)
         else:
-            filename = 'builtin_model_{}'.format(self.name_idx)
+            filename = f"builtin_model_{self.name_idx}"
             self.name_idx += 1
         self.filename_to_model[filename] = model
 
@@ -80,7 +82,7 @@ def __setitem__(self, filename, model):
         self.filename_to_model[filename] = model
 
 
-class GlobalModelRepository(object):
+class GlobalModelRepository:
     """
     This class has the responsibility to hold two ModelRepository objects:
 
@@ -124,8 +126,15 @@ def remove_models(self, models):
             self.remove_model(m)
 
     def load_models_using_filepattern(
-            self, filename_pattern, model, glob_args, is_main_model=False,
-            encoding='utf-8', add_to_local_models=True, model_params=None):
+        self,
+        filename_pattern,
+        model,
+        glob_args,
+        is_main_model=False,
+        encoding="utf-8",
+        add_to_local_models=True,
+        model_params=None,
+    ):
         """
         Add a new model to all relevant objects.
 
@@ -139,6 +148,7 @@ def load_models_using_filepattern(
             the list of loaded models
         """
         from textx import get_metamodel
+
         if model is not None:
             self.update_model_in_repo_based_on_filename(model)
             the_metamodel = get_metamodel(model)  # default metamodel
@@ -146,22 +156,34 @@ def load_models_using_filepattern(
             the_metamodel = None
         filenames = glob.glob(filename_pattern, **glob_args)
         if len(filenames) == 0:
-            raise IOError(
-                errno.ENOENT, os.strerror(errno.ENOENT), filename_pattern)
+            raise OSError(errno.ENOENT, os.strerror(errno.ENOENT), filename_pattern)
         loaded_models = []
         for filename in filenames:
             the_metamodel = metamodel_for_file_or_default_metamodel(
-                filename, the_metamodel)
+                filename, the_metamodel
+            )
             loaded_models.append(
-                self.load_model(the_metamodel, filename, is_main_model,
-                                encoding=encoding,
-                                add_to_local_models=add_to_local_models,
-                                model_params=model_params))
+                self.load_model(
+                    the_metamodel,
+                    filename,
+                    is_main_model,
+                    encoding=encoding,
+                    add_to_local_models=add_to_local_models,
+                    model_params=model_params,
+                )
+            )
         return loaded_models
 
     def load_model_using_search_path(
-            self, filename, model, search_path, is_main_model=False,
-            encoding='utf8', add_to_local_models=True, model_params=None):
+        self,
+        filename,
+        model,
+        search_path,
+        is_main_model=False,
+        encoding="utf8",
+        add_to_local_models=True,
+        model_params=None,
+    ):
         """
         Add a new model to all relevant objects
 
@@ -175,31 +197,37 @@ def load_model_using_search_path(
             the loaded model
         """
         from textx import get_metamodel
+
         if model:
             self.update_model_in_repo_based_on_filename(model)
         for the_path in search_path:
             full_filename = join(the_path, filename)
             # print(full_filename)
             if exists(full_filename):
-                if model is not None:
-                    the_metamodel = get_metamodel(model)
-                else:
-                    the_metamodel = None
+                the_metamodel = get_metamodel(model) if model is not None else None
                 the_metamodel = metamodel_for_file_or_default_metamodel(
-                    filename, the_metamodel)
-                return self.load_model(the_metamodel,
-                                       full_filename,
-                                       is_main_model,
-                                       encoding=encoding,
-                                       add_to_local_models=add_to_local_models,
-                                       model_params=model_params)
-
-        raise IOError(
-            errno.ENOENT, os.strerror(errno.ENOENT), filename)
+                    filename, the_metamodel
+                )
+                return self.load_model(
+                    the_metamodel,
+                    full_filename,
+                    is_main_model,
+                    encoding=encoding,
+                    add_to_local_models=add_to_local_models,
+                    model_params=model_params,
+                )
+
+        raise OSError(errno.ENOENT, os.strerror(errno.ENOENT), filename)
 
     def load_model(
-            self, the_metamodel, filename, is_main_model, encoding='utf-8',
-            add_to_local_models=True, model_params=None):
+        self,
+        the_metamodel,
+        filename,
+        is_main_model,
+        encoding="utf-8",
+        add_to_local_models=True,
+        model_params=None,
+    ):
         """
         Load a single model
 
@@ -210,8 +238,7 @@ def load_model(
         Returns:
             the loaded/cached model
         """
-        assert model_params is not None, \
-            "model_params needs to be specified"
+        assert model_params is not None, "model_params needs to be specified"
 
         filename = abspath(filename)
         if not self.local_models.has_model(filename):
@@ -223,10 +250,15 @@ def load_model(
                 # all models loaded here get their references resolved from the
                 # root model
                 new_model = the_metamodel.internal_model_from_file(
-                    filename, pre_ref_resolution_callback=lambda
-                    other_model: self.pre_ref_resolution_callback(other_model),
-                    is_main_model=is_main_model, encoding=encoding,
-                    model_params=model_params)
+                    filename,
+                    pre_ref_resolution_callback=
+                        lambda other_model: self.pre_ref_resolution_callback(
+                        other_model
+                    ),
+                    is_main_model=is_main_model,
+                    encoding=encoding,
+                    model_params=model_params,
+                )
                 self.all_models[filename] = new_model
             # print("ADDING {}".format(filename))
             if add_to_local_models:
@@ -260,13 +292,13 @@ def update_model_in_repo_based_on_filename(self, model):
                     # print("UPDATED/CACHED {}".format(fn))
                     return fn
             i = 0
-            while self.all_models.has_model("anonymous{}".format(i)):
+            while self.all_models.has_model(f"anonymous{i}"):
                 i += 1
-            myfilename = "anonymous{}".format(i)
+            myfilename = f"anonymous{i}"
             self.all_models[myfilename] = model
         else:
             myfilename = abspath(model._tx_filename)
-            if (not self.all_models.has_model(myfilename)):
+            if not self.all_models.has_model(myfilename):
                 self.all_models[myfilename] = model
         # print("UPDATED/ADDED/CACHED {}".format(myfilename))
         return myfilename
@@ -283,14 +315,13 @@ def pre_ref_resolution_callback(self, other_model):
         """
         filename = other_model._tx_filename
         # print("PRE-CALLBACK -> {}".format(filename))
-        assert (filename)
+        assert filename
         filename = abspath(filename)
-        other_model._tx_model_repository = \
-            GlobalModelRepository(self.all_models)
+        other_model._tx_model_repository = GlobalModelRepository(self.all_models)
         self.all_models[filename] = other_model
 
 
-class ModelLoader(object):
+class ModelLoader:
     """
     This class is an interface to mark a scope provider as an additional model
     loader.
@@ -330,7 +361,7 @@ def get_included_models(model):
     Returns:
         a list of all models
     """
-    if (hasattr(model, "_tx_model_repository")):
+    if hasattr(model, "_tx_model_repository"):
         models = list(model._tx_model_repository.all_models)
         if model not in models:
             models.append(model)
@@ -353,15 +384,14 @@ def is_file_included(filename, model):
         (Note: if no _tx_model_repository is present,
         the function always returns False)
     """
-    if (hasattr(model, "_tx_model_repository")):
+    if hasattr(model, "_tx_model_repository"):
         all_entries = model._tx_model_repository.all_models
         return all_entries.has_model(filename)
     else:
         return False
 
 
-def remove_models_from_repositories(models,
-                                    models_to_be_removed):
+def remove_models_from_repositories(models, models_to_be_removed):
     """
     Remove models from all relevant repositories (_tx_model_repository
     of models and related metamodel(s), if applicable).
@@ -377,7 +407,6 @@ def remove_models_from_repositories(models,
     assert isinstance(models, list)
     for model in models:
         if hasattr(model._tx_metamodel, "_tx_model_repository"):
-            model._tx_metamodel. \
-                _tx_model_repository.remove_models(models_to_be_removed)
+            model._tx_metamodel._tx_model_repository.remove_models(models_to_be_removed)
         if hasattr(model, "_tx_model_repository"):
             model._tx_model_repository.remove_models(models_to_be_removed)
diff --git a/textx/scoping/providers.py b/textx/scoping/providers.py
index 9bf30d9a4..5ec1158cb 100644
--- a/textx/scoping/providers.py
+++ b/textx/scoping/providers.py
@@ -5,9 +5,10 @@
 # License: MIT License
 #######################################################################
 
-from os.path import dirname, abspath, join, isabs
-from textx.exceptions import TextXSemanticError
+from os.path import abspath, dirname, isabs, join
+
 import textx.scoping as scoping
+from textx.exceptions import TextXSemanticError
 from textx.scoping import Postponed
 
 """
@@ -19,7 +20,7 @@
 """
 
 
-class PlainName(object):
+class PlainName:
     """
     plain name scope provider
     """
@@ -47,7 +48,7 @@ def __call__(self, obj, attr, obj_ref):
         Returns:
             the resolved reference or None
         """
-        from textx.const import RULE_COMMON, RULE_ABSTRACT
+        from textx.const import RULE_ABSTRACT, RULE_COMMON
         from textx.model import ObjCrossRef
         from textx.scoping.tools import get_parser
 
@@ -57,8 +58,9 @@ def __call__(self, obj, attr, obj_ref):
         assert type(obj_ref) is ObjCrossRef, type(obj_ref)
 
         if get_parser(obj).debug:
-            get_parser(obj).dprint("Resolving obj crossref: {}:{}"
-                                   .format(obj_ref.cls, obj_ref.obj_name))
+            get_parser(obj).dprint(
+                f"Resolving obj crossref: {obj_ref.cls}:{obj_ref.obj_name}"
+            )
 
         def _inner_resolve_link_rule_ref(cls, obj_name):
             """
@@ -66,11 +68,10 @@ def _inner_resolve_link_rule_ref(cls, obj_name):
             """
             if cls._tx_type is RULE_ABSTRACT:
                 for inherited in cls._tx_inh_by:
-                    result = _inner_resolve_link_rule_ref(inherited,
-                                                          obj_name)
+                    result = _inner_resolve_link_rule_ref(inherited, obj_name)
                     if result:
                         return result
-            elif cls._tx_type == RULE_COMMON:
+            elif cls._tx_type == RULE_COMMON and id(cls) in get_parser(obj)._instances:
                 # TODO make this code exchangable
                 # allow to know the current attribute (model location for
                 # namespace) and to navigate through the whole model...
@@ -81,33 +82,36 @@ def _inner_resolve_link_rule_ref(cls, obj_name):
                 # - needs: .current reference (in the model)
                 #          .the model (?)
                 # - provides: the resolved object or None
-                if id(cls) in get_parser(obj)._instances:
-                    objs = get_parser(obj)._instances[id(cls)]
-                    return objs.get(obj_name)
+                objs = get_parser(obj)._instances[id(cls)]
+                return objs.get(obj_name)
 
         if self.multi_metamodel_support:
-            from textx import get_model, get_children
-            from textx import textx_isinstance
+            from textx import get_children, get_model, textx_isinstance
+
             result_lst = get_children(
-                lambda x:
-                hasattr(x, "name") and x.name == obj_ref.obj_name
-                and textx_isinstance(x, obj_ref.cls), get_model(obj))
+                lambda x: hasattr(x, "name")
+                and x.name == obj_ref.obj_name
+                and textx_isinstance(x, obj_ref.cls),
+                get_model(obj),
+            )
             if len(result_lst) == 1:
                 result = result_lst[0]
             elif len(result_lst) > 1:
                 line, col = get_parser(obj).pos_to_linecol(obj_ref.position)
                 raise TextXSemanticError(
-                    "name {} is not unique.".format(obj_ref.obj_name),
-                    line=line, col=col, filename=get_model(obj)._tx_filename)
+                    f"name {obj_ref.obj_name} is not unique.",
+                    line=line,
+                    col=col,
+                    filename=get_model(obj)._tx_filename,
+                )
             else:
                 result = None
         else:
-            result = _inner_resolve_link_rule_ref(obj_ref.cls,
-                                                  obj_ref.obj_name)
+            result = _inner_resolve_link_rule_ref(obj_ref.cls, obj_ref.obj_name)
         return result  # error handled outside
 
 
-class FQN(object):
+class FQN:
     """
     fully qualified name scope provider
     """
@@ -160,34 +164,35 @@ def _find_obj_fqn(p, fqn_name, cls):
             """
 
             def find_obj(parent, name):
-                if parent is not current_obj and \
-                        self.scope_redirection_logic is not None:
+                if parent is not current_obj and self.scope_redirection_logic is not None:
                     from textx.scoping import Postponed
+
                     res = self.scope_redirection_logic(parent)
-                    assert res is not None, \
-                        "scope_redirection_logic must not return None"
+                    assert res is not None, "scope_redirection_logic must not return None"
                     if type(res) is Postponed:
                         return res
                     for m in res:
                         return_value = find_obj(m, name)
                         if return_value is not None:
                             return return_value
-                for attr in [a for a in parent.__dict__ if
-                             not a.startswith('__') and not
-                             a.startswith('_tx_') and not
-                             callable(getattr(parent, a))]:
+                for attr in [
+                    a
+                    for a in parent.__dict__
+                    if not a.startswith("__")
+                    and not a.startswith("_tx_")
+                    and not callable(getattr(parent, a))
+                ]:
                     obj = getattr(parent, attr)
                     if isinstance(obj, (list, tuple)):
                         for innerobj in obj:
-                            if hasattr(innerobj, "name") \
-                                    and innerobj.name == name:
+                            if hasattr(innerobj, "name") and innerobj.name == name:
                                 return innerobj
                     else:
                         if hasattr(obj, "name") and obj.name == name:
                             return obj
                 return None
 
-            for n in fqn_name.split('.'):
+            for n in fqn_name.split("."):
                 obj = find_obj(p, n)
                 if obj is not None:
                     if type(obj) is Postponed:
@@ -197,6 +202,7 @@ def find_obj(parent, name):
                     return None
 
             from textx import textx_isinstance
+
             if textx_isinstance(obj, cls):
                 return p
             else:
@@ -227,6 +233,7 @@ def _find_referenced_obj(p, name, cls):
                 # else continue to next parent or return None
 
         from textx.model import ObjCrossRef
+
         assert type(obj_ref) is ObjCrossRef, type(obj_ref)
         obj_cls, obj_name = obj_ref.cls, obj_ref.obj_name
         return _find_referenced_obj(current_obj, obj_name, obj_cls)
@@ -250,9 +257,15 @@ class ImportURI(scoping.ModelLoader):
     yield a filename or a filename pattern.
     """
 
-    def __init__(self, scope_provider, glob_args=None, search_path=None,
-                 importAs=False, importURI_converter=None,
-                 importURI_to_scope_name=None):
+    def __init__(
+        self,
+        scope_provider,
+        glob_args=None,
+        search_path=None,
+        importAs=False,
+        importURI_converter=None,
+        importURI_to_scope_name=None,
+    ):
         """
         Creates a new ImportURI Provider.
         Args:
@@ -276,11 +289,11 @@ def __init__(self, scope_provider, glob_args=None, search_path=None,
 
         """
         from textx.scoping import ModelLoader
+
         ModelLoader.__init__(self)
         self.scope_provider = scope_provider
         if (glob_args is not None) and (search_path is not None):
-            raise Exception("you cannot use globbing together with a "
-                            "search path")
+            raise Exception("you cannot use globbing together with a " "search path")
         self.glob_args = {}
         self.search_path = search_path
         self.importAs = importAs
@@ -297,53 +310,62 @@ def set_glob_args(self, glob_args):
 
     def _load_referenced_models(self, model, encoding):
         from textx.model import get_children
+
         visited = []
         for obj in get_children(
-                lambda x: hasattr(x, "importURI") and x not in visited, model):
+            lambda x: hasattr(x, "importURI") and x not in visited, model
+        ):
             add_to_local_models = True
             if self.importURI_to_scope_name is not None:
                 obj.name = self.importURI_to_scope_name(obj)
                 # print("setting name to {}".format(obj.name))
-            if hasattr(obj, "name"):
-                if obj.name is not None and obj.name != "":
-                    add_to_local_models = not self.importAs
+            if hasattr(obj, "name") and obj.name is not None and obj.name != "":
+                add_to_local_models = not self.importAs
 
             visited.append(obj)
             if self.search_path is not None:
                 # search_path based i/o:
-                my_search_path = \
-                    [dirname(model._tx_filename)] + self.search_path
-                loaded_model = \
-                    model._tx_model_repository.load_model_using_search_path(
-                        self.importURI_converter(obj.importURI), model=model,
-                        search_path=my_search_path, encoding=encoding,
-                        add_to_local_models=add_to_local_models,
-                        model_params=model._tx_model_params)
+                my_search_path = [dirname(model._tx_filename)] + self.search_path
+                loaded_model = model._tx_model_repository.load_model_using_search_path(
+                    self.importURI_converter(obj.importURI),
+                    model=model,
+                    search_path=my_search_path,
+                    encoding=encoding,
+                    add_to_local_models=add_to_local_models,
+                    model_params=model._tx_model_params,
+                )
                 obj._tx_loaded_models = [loaded_model]
 
             else:
                 # globing based i/o:
                 basedir = dirname(model._tx_filename)
                 filename_pattern = abspath(
-                    join(basedir, self.importURI_converter(obj.importURI)))
+                    join(basedir, self.importURI_converter(obj.importURI))
+                )
 
-                obj._tx_loaded_models = \
+                obj._tx_loaded_models = (
                     model._tx_model_repository.load_models_using_filepattern(
-                        filename_pattern, model=model,
-                        glob_args=self.glob_args, encoding=encoding,
+                        filename_pattern,
+                        model=model,
+                        glob_args=self.glob_args,
+                        encoding=encoding,
                         add_to_local_models=add_to_local_models,
-                        model_params=model._tx_model_params)
+                        model_params=model._tx_model_params,
+                    )
+                )
 
-    def load_models(self, model, encoding='utf-8'):
+    def load_models(self, model, encoding="utf-8"):
         from textx.model import get_metamodel
         from textx.scoping import GlobalModelRepository
+
         # do we already have loaded models (analysis)? No -> check/load them
         if hasattr(model, "_tx_model_repository"):
             pass
         else:
             if hasattr(get_metamodel(model), "_tx_model_repository"):
-                model_repository = GlobalModelRepository(get_metamodel(
-                    model)._tx_model_repository.all_models)
+                model_repository = GlobalModelRepository(
+                    get_metamodel(model)._tx_model_repository.all_models
+                )
             else:
                 model_repository = GlobalModelRepository()
             model._tx_model_repository = model_repository
@@ -351,6 +373,7 @@ def load_models(self, model, encoding='utf-8'):
 
     def __call__(self, obj, attr, obj_ref):
         from textx.model import ObjCrossRef, get_model
+
         assert type(obj_ref) is ObjCrossRef, type(obj_ref)
         # cls, obj_name = obj_ref.cls, obj_ref.obj_name
 
@@ -405,22 +428,34 @@ class FQNImportURI(ImportURI):
     See also: "test_importURI_variations.py".
     """
 
-    def __init__(self, glob_args=None, search_path=None, importAs=False,
-                 importURI_converter=None, importURI_to_scope_name=None,
-                 scope_redirection_logic=None):
+    def __init__(
+        self,
+        glob_args=None,
+        search_path=None,
+        importAs=False,
+        importURI_converter=None,
+        importURI_to_scope_name=None,
+        scope_redirection_logic=None,
+    ):
         if importAs:
+
             def my_scope_redirection_logic_def(obj):
                 return follow_loaded_models_scope_redirection_logic(
-                    obj, scope_redirection_logic)
+                    obj, scope_redirection_logic
+                )
+
             my_scope_redirection_logic = my_scope_redirection_logic_def
         else:
             my_scope_redirection_logic = scope_redirection_logic
-        ImportURI.__init__(self, FQN(
-            scope_redirection_logic=my_scope_redirection_logic),
+        ImportURI.__init__(
+            self,
+            FQN(scope_redirection_logic=my_scope_redirection_logic),
             glob_args=glob_args,
-            search_path=search_path, importAs=importAs,
+            search_path=search_path,
+            importAs=importAs,
             importURI_converter=importURI_converter,
-            importURI_to_scope_name=importURI_to_scope_name)
+            importURI_to_scope_name=importURI_to_scope_name,
+        )
 
 
 class PlainNameImportURI(ImportURI):
@@ -428,11 +463,14 @@ class PlainNameImportURI(ImportURI):
     scope provider with ImportURI and PlainName names
     """
 
-    def __init__(self, glob_args=None, search_path=None,
-                 importURI_converter=None):
-        ImportURI.__init__(self, PlainName(), glob_args=glob_args,
-                           search_path=search_path,
-                           importURI_converter=importURI_converter)
+    def __init__(self, glob_args=None, search_path=None, importURI_converter=None):
+        ImportURI.__init__(
+            self,
+            PlainName(),
+            glob_args=glob_args,
+            search_path=search_path,
+            importURI_converter=importURI_converter,
+        )
 
 
 class GlobalRepo(ImportURI):
@@ -472,13 +510,17 @@ def register_models(self, filename_pattern):
 
     def _load_referenced_models(self, model, encoding):
         for filename_pattern in self.filename_pattern_list:
-            if not isabs(filename_pattern) and \
-                    'project_root' in model._tx_model_params:
+            if not isabs(filename_pattern) and "project_root" in model._tx_model_params:
                 filename_pattern = join(
-                    model._tx_model_params['project_root'], filename_pattern)
+                    model._tx_model_params["project_root"], filename_pattern
+                )
             model._tx_model_repository.load_models_using_filepattern(
-                filename_pattern, model=model, glob_args=self.glob_args,
-                encoding=encoding, model_params=model._tx_model_params)
+                filename_pattern,
+                model=model,
+                glob_args=self.glob_args,
+                encoding=encoding,
+                model_params=model._tx_model_params,
+            )
         for m in self.models_to_be_added_directly:
             model._tx_model_repository._add_model(m)
 
@@ -491,8 +533,9 @@ def add_model(self, model):
         """
         self.models_to_be_added_directly.append(model)
 
-    def load_models_in_model_repo(self, global_model_repo=None,
-                                  encoding='utf-8', **kwargs):
+    def load_models_in_model_repo(
+        self, global_model_repo=None, encoding="utf-8", **kwargs
+    ):
         """
         load all registered models (called explicitly from
         the user and not as an automatic activity).
@@ -520,9 +563,12 @@ def load_models_in_model_repo(self, global_model_repo=None,
             global_model_repo = textx.scoping.GlobalModelRepository()
         for filename_pattern in self.filename_pattern_list:
             global_model_repo.load_models_using_filepattern(
-                filename_pattern, model=None, glob_args=self.glob_args,
-                is_main_model=True, encoding=encoding,
-                model_params=ModelParams(kwargs)
+                filename_pattern,
+                model=None,
+                glob_args=self.glob_args,
+                is_main_model=True,
+                encoding=encoding,
+                model_params=ModelParams(kwargs),
             )
         return global_model_repo
 
@@ -533,8 +579,7 @@ class FQNGlobalRepo(GlobalRepo):
     """
 
     def __init__(self, filename_pattern=None, glob_args=None):
-        GlobalRepo.__init__(self, FQN(), filename_pattern,
-                            glob_args=glob_args)
+        GlobalRepo.__init__(self, FQN(), filename_pattern, glob_args=glob_args)
 
 
 class PlainNameGlobalRepo(GlobalRepo):
@@ -543,11 +588,10 @@ class PlainNameGlobalRepo(GlobalRepo):
     """
 
     def __init__(self, filename_pattern=None, glob_args=None):
-        GlobalRepo.__init__(
-            self, PlainName(), filename_pattern, glob_args=glob_args)
+        GlobalRepo.__init__(self, PlainName(), filename_pattern, glob_args=glob_args)
 
 
-class RelativeName(object):
+class RelativeName:
     """
     allows to implement a class-method-instance-like scoping:
      - define a class with methods
@@ -581,8 +625,9 @@ def get_reference_propositions(self, obj, attr, name_part):
         Returns:
             the list of objects representing the proposed references
         """
-        from textx.scoping.tools import resolve_model_path
         from textx import textx_isinstance
+        from textx.scoping.tools import resolve_model_path
+
         obj_list = resolve_model_path(obj, self.path_to_container_object)
         if type(obj_list) is Postponed:
             self.postponed_counter += 1
@@ -592,12 +637,14 @@ def get_reference_propositions(self, obj, attr, name_part):
         # the RelativeName object).
         if not isinstance(obj_list, list):
             from textx.exceptions import TextXError
+
             raise TextXError(
-                "expected path to list in the model ({})".format(
-                    self.path_to_container_object))
+                f"expected path to list in the model ({self.path_to_container_object})"
+            )
         obj_list = filter(
-            lambda x: textx_isinstance(x, attr.cls)
-            and x.name.find(name_part) >= 0, obj_list)
+            lambda x: textx_isinstance(x, attr.cls) and x.name.find(name_part) >= 0,
+            obj_list,
+        )
 
         return list(obj_list)
 
@@ -612,7 +659,7 @@ def __call__(self, obj, attr, obj_ref):
             return None
 
 
-class ExtRelativeName(object):
+class ExtRelativeName:
     """
     Similar as RelativeName.
     Here you specify separately
@@ -621,8 +668,7 @@ class ExtRelativeName(object):
     - how to find inherited/chained classes (starting from a class).
     """
 
-    def __init__(self, path_to_definition_object, path_to_target,
-                 path_to_extension):
+    def __init__(self, path_to_definition_object, path_to_target, path_to_extension):
         self.path_to_definition_object = path_to_definition_object
         self.path_to_target = path_to_target
         self.path_to_extension = path_to_extension
@@ -639,15 +685,18 @@ def get_reference_propositions(self, obj, attr, name_part):
         Returns:
             the list of objects representing the proposed references
         """
-        from textx.scoping.tools import resolve_model_path
         from textx import textx_isinstance
+
         # find all all "connected" objects
         # (e.g. find all classes: the most derived
         # class, its base, the base of its base, etc.)
-        from textx.scoping.tools import get_list_of_concatenated_objects
+        from textx.scoping.tools import (
+            get_list_of_concatenated_objects,
+            resolve_model_path,
+        )
+
         def_obj = resolve_model_path(obj, self.path_to_definition_object)
-        def_objs = get_list_of_concatenated_objects(
-            def_obj, self.path_to_extension)
+        def_objs = get_list_of_concatenated_objects(def_obj, self.path_to_extension)
         # for all containing classes, collect all
         # objects to be looked up (e.g. methods)
         obj_list = []
@@ -661,12 +710,17 @@ def get_reference_propositions(self, obj, attr, name_part):
             # expected to point to  alist
             if not isinstance(tmp_list, list):
                 from textx.exceptions import TextXError
+
                 raise TextXError(
-                    "expected path to list in the model ({})".format(
-                        self.path_to_target))
-            tmp_list = list(filter(
-                lambda x: textx_isinstance(x, attr.cls)
-                and x.name.find(name_part) >= 0, tmp_list))
+                    f"expected path to list in the model ({self.path_to_target})"
+                )
+            tmp_list = list(
+                filter(
+                    lambda x: textx_isinstance(x, attr.cls)
+                    and x.name.find(name_part) >= 0,
+                    tmp_list,
+                )
+            )
             obj_list = obj_list + tmp_list
 
         return list(obj_list)
diff --git a/textx/scoping/rrel.py b/textx/scoping/rrel.py
index 47005c5cf..18815584c 100644
--- a/textx/scoping/rrel.py
+++ b/textx/scoping/rrel.py
@@ -1,29 +1,28 @@
-from __future__ import unicode_literals
-from arpeggio import PTNodeVisitor, visit_parse_tree
-from arpeggio import Optional, EOF
-from arpeggio import ZeroOrMore as ArpeggioZeroOrMore
+from arpeggio import EOF, Optional, PTNodeVisitor, visit_parse_tree
 from arpeggio import RegExMatch as _
+from arpeggio import ZeroOrMore as ArpeggioZeroOrMore
 
 
 def rrel_id():
-    return _(r'[^\d\W]\w*\b')  # from lang.py
+    return _(r"[^\d\W]\w*\b")  # from lang.py
 
 
 def rrel_parent():
-    return 'parent', '(', rrel_id, ')'
+    return "parent", "(", rrel_id, ")"
 
 
 def rrel_navigation():
     from textx.lang import string_value
-    return [(Optional('~'), rrel_id), (Optional(string_value), '~', rrel_id)]
+
+    return [(Optional("~"), rrel_id), (Optional(string_value), "~", rrel_id)]
 
 
 def rrel_brackets():
-    return '(', rrel_sequence, ')'
+    return "(", rrel_sequence, ")"
 
 
 def rrel_dots():
-    return _(r'\.+')
+    return _(r"\.+")
 
 
 def rrel_path_element():
@@ -31,14 +30,18 @@ def rrel_path_element():
 
 
 def rrel_zero_or_more():
-    return rrel_path_element, '*'
+    return rrel_path_element, "*"
 
 
 def rrel_path():
-    return [(Optional(['^', rrel_dots]),
-            ArpeggioZeroOrMore([rrel_zero_or_more, rrel_path_element], '.'),
-             [rrel_zero_or_more, rrel_path_element]),
-            ['^', rrel_dots]]
+    return [
+        (
+            Optional(["^", rrel_dots]),
+            ArpeggioZeroOrMore([rrel_zero_or_more, rrel_path_element], "."),
+            [rrel_zero_or_more, rrel_path_element],
+        ),
+        ["^", rrel_dots],
+    ]
 
 
 def rrel_sequence():
@@ -46,19 +49,20 @@ def rrel_sequence():
 
 
 def rrel_expression():
-    return Optional(_(r'\+[mp]+:')), rrel_sequence
+    return Optional(_(r"\+[mp]+:")), rrel_sequence
 
 
 def rrel_standalone():
     return rrel_expression, EOF
 
 
-class RRELBase(object):
+class RRELBase:
     def __init__(self):
         pass
 
-    def get_next_matches(self, obj, lookup_list, allowed, matched_path,
-                         first_element=False):
+    def get_next_matches(
+        self, obj, lookup_list, allowed, matched_path, first_element=False
+    ):
         """
         This function yields potential matches encountered along the
         requested RREL.
@@ -86,7 +90,8 @@ def get_next_matches(self, obj, lookup_list, allowed, matched_path,
             return  # recursion stopper
 
         obj, lookup_list, matched_path = self.apply(
-            obj, lookup_list, matched_path, first_element)
+            obj, lookup_list, matched_path, first_element
+        )
         if obj is None:
             return
         elif isinstance(obj, list):
@@ -99,11 +104,11 @@ def get_next_matches(self, obj, lookup_list, allowed, matched_path,
 
 class RRELParent(RRELBase):
     def __init__(self, type):
-        super(RRELParent, self).__init__()
+        super().__init__()
         self.type = type
 
     def __repr__(self):
-        return 'parent({})'.format(self.type)
+        return f"parent({self.type})"
 
     def start_locally(self):
         return True
@@ -121,6 +126,7 @@ def apply(self, obj, lookup_list, matched_path, first_element):
             The parent of the specified type or None.
         """
         from textx import get_metamodel, textx_isinstance
+
         t = get_metamodel(obj)[self.type]
         while hasattr(obj, "parent"):
             obj = obj.parent
@@ -131,7 +137,7 @@ def apply(self, obj, lookup_list, matched_path, first_element):
 
 class RRELNavigation(RRELBase):
     def __init__(self, name, consume_name, fixed_name):
-        super(RRELNavigation, self).__init__()
+        super().__init__()
         self.name = name
         self.consume_name = consume_name
         self.fixed_name = fixed_name
@@ -142,7 +148,7 @@ def __repr__(self):
             assert not self.consume_name
             return "'" + self.fixed_name + "'~" + self.name
         else:
-            return self.name if self.consume_name else '~' + self.name
+            return self.name if self.consume_name else "~" + self.name
 
     def start_locally(self):
         return False
@@ -161,21 +167,23 @@ def apply(self, obj, lookup_list, matched_path, first_element):
             Postponed, None, or a list (if a list has to be processed).
         """
         assert self.rrel_expression is not None
-        from textx.scoping.tools import needs_to_be_resolved
         from textx.scoping import Postponed
+        from textx.scoping.tools import needs_to_be_resolved
+
         if first_element:
             from textx import get_model
+
             obj = get_model(obj)
 
         start = [obj]
-        if not hasattr(obj, "parent"):  # am I a root model node?
-            if self.rrel_expression.importURI:
-                if hasattr(obj, "_tx_model_repository"):
-                    for m in obj._tx_model_repository.local_models:
-                        start.append(m)
-                if obj._tx_metamodel.builtin_models:
-                    for m in obj._tx_metamodel.builtin_models:
-                        start.append(m)
+        # am I a root model node?
+        if not hasattr(obj, "parent") and self.rrel_expression.importURI:
+            if hasattr(obj, "_tx_model_repository"):
+                for m in obj._tx_model_repository.local_models:
+                    start.append(m)
+            if obj._tx_metamodel.builtin_models:
+                for m in obj._tx_metamodel.builtin_models:
+                    start.append(m)
 
         if len(lookup_list) == 0 and self.consume_name:
             return None, lookup_list, matched_path
@@ -191,21 +199,34 @@ def lookup(obj):
                     if not isinstance(target, list):
                         target = [target]
                     if self.fixed_name is not None:
-                        lst = list(filter(lambda x: hasattr(
-                            x, "name") and getattr(
-                            x, "name") == self.fixed_name, target))
+                        lst = list(
+                            filter(
+                                lambda x: hasattr(x, "name")
+                                and x.name == self.fixed_name,
+                                target,
+                            )
+                        )
                         if len(lst) > 0:
-                            return lst[0], lookup_list, matched_path + [
-                                lst[0]]  # return obj
+                            return (
+                                lst[0],
+                                lookup_list,
+                                matched_path + [lst[0]],
+                            )  # return obj
                         else:
                             return None, lookup_list, matched_path  # return None
                     else:
-                        lst = list(filter(lambda x: hasattr(
-                            x, "name") and getattr(
-                            x, "name") == lookup_list[0], target))
+                        lst = list(
+                            filter(
+                                lambda x: hasattr(x, "name") and x.name == lookup_list[0],
+                                target,
+                            )
+                        )
                         if len(lst) > 0:
-                            return lst[0], lookup_list[1:], matched_path + [
-                                lst[0]]  # return obj
+                            return (
+                                lst[0],
+                                lookup_list[1:],
+                                matched_path + [lst[0]],
+                            )  # return obj
                         else:
                             return None, lookup_list, matched_path  # return None
             else:
@@ -213,7 +234,7 @@ def lookup(obj):
 
         for start_obj in start:
             res, res_lookup_list, res_lookup_path = lookup(start_obj)
-            if (res):
+            if res:
                 return res, res_lookup_list, res_lookup_path
 
         return None, lookup_list, matched_path
@@ -221,7 +242,7 @@ def lookup(obj):
 
 class RRELBrackets(RRELBase):
     def __init__(self, oc):
-        super(RRELBrackets, self).__init__()
+        super().__init__()
         assert isinstance(oc, RRELSequence)
         self.seq = oc
 
@@ -232,24 +253,25 @@ def start_at_root(self):
         return self.seq.start_at_root()
 
     def __repr__(self):
-        return '(' + str(self.seq) + ')'
+        return "(" + str(self.seq) + ")"
 
-    def get_next_matches(self, obj, lookup_list, allowed, matched_path,
-                         first_element=False):
+    def get_next_matches(
+        self, obj, lookup_list, allowed, matched_path, first_element=False
+    ):
         if not allowed(obj, lookup_list, self):  # also adjusts visited objs
             return  # recursion stopper
-        for iobj, ilookup_list, imatched_path in self.seq.get_next_matches(
-                obj, lookup_list, allowed, matched_path, first_element):
-            yield iobj, ilookup_list, imatched_path
+        yield from self.seq.get_next_matches(
+            obj, lookup_list, allowed, matched_path, first_element
+        )
 
 
 class RRELDots(RRELBase):
     def __init__(self, num):
-        super(RRELDots, self).__init__()
+        super().__init__()
         self.num = num
 
     def __repr__(self):
-        return ('.' * self.num)
+        return "." * self.num
 
     def start_locally(self):
         return True
@@ -278,11 +300,11 @@ def apply(self, obj, lookup_list, matched_path, first_element):
 
 class RRELSequence(RRELBase):
     def __init__(self, paths):
-        super(RRELSequence, self).__init__()
+        super().__init__()
         self.paths = paths
 
     def __repr__(self):
-        return ','.join(map(lambda x: str(x), self.paths))
+        return ",".join(map(lambda x: str(x), self.paths))
 
     def start_locally(self):
         res = False
@@ -296,22 +318,22 @@ def start_at_root(self):
             res = res or p.start_at_root()
         return res
 
-    def get_next_matches(self, obj, lookup_list, allowed, matched_path,
-                         first_element=False):
+    def get_next_matches(
+        self, obj, lookup_list, allowed, matched_path, first_element=False
+    ):
         if not allowed(obj, lookup_list, self):  # also adjusts visited objs
             return  # recursion stopper
         for ip in self.paths:
-            for iobj, ilookup_list, imatched_path in ip.get_next_matches(
-                    obj, lookup_list, allowed, matched_path, first_element):
-                yield iobj, ilookup_list, imatched_path
+            yield from ip.get_next_matches(
+                obj, lookup_list, allowed, matched_path, first_element
+            )
 
 
 class RRELZeroOrMore(RRELBase):
     def __init__(self, path_element):
-        super(RRELZeroOrMore, self).__init__()
+        super().__init__()
         if not isinstance(path_element, RRELBrackets):
-            path_element = RRELBrackets(RRELSequence(
-                [RRELPath([path_element])]))
+            path_element = RRELBrackets(RRELSequence([RRELPath([path_element])]))
         self.path_element = path_element
         assert isinstance(self.path_element, RRELBrackets)
 
@@ -322,15 +344,15 @@ def start_at_root(self):
         return self.path_element.start_at_root()
 
     def __repr__(self):
-        return str(self.path_element) + '*'
+        return str(self.path_element) + "*"
 
-    def get_next_matches(self, obj, lookup_list, allowed, matched_path,
-                         first_element=False):
+    def get_next_matches(
+        self, obj, lookup_list, allowed, matched_path, first_element=False
+    ):
         assert isinstance(self.path_element, RRELBrackets)
         from textx.scoping import Postponed
 
-        def get_from_zero_or_more(obj, lookup_list, matched_path,
-                                  first_element=False):
+        def get_from_zero_or_more(obj, lookup_list, matched_path, first_element=False):
             assert self.start_locally() or self.start_at_root()  # or, not xor
             if not allowed(obj, lookup_list, self):  # also adjusts visited objs
                 return  # recursion stopper
@@ -339,25 +361,28 @@ def get_from_zero_or_more(obj, lookup_list, matched_path,
                     yield obj, lookup_list, matched_path
                 if self.start_at_root():
                     from textx import get_model
+
                     yield get_model(obj), lookup_list, matched_path
             else:
                 yield obj, lookup_list, matched_path
             assert isinstance(self.path_element.seq, RRELSequence)
-            for iobj, ilookup_list, \
-                imatched_path in self.path_element.seq.get_next_matches(
-                    obj, lookup_list, allowed, matched_path,
-                    first_element=first_element):
+            for (
+                iobj,
+                ilookup_list,
+                imatched_path,
+            ) in self.path_element.seq.get_next_matches(
+                obj, lookup_list, allowed, matched_path, first_element=first_element
+            ):
                 if isinstance(iobj, Postponed):
                     yield iobj, ilookup_list, imatched_path  # found postponed
                     return
                 # yield from
-                for iiobj, iilookup_list, iimatched_path in get_from_zero_or_more(
-                        iobj, ilookup_list, imatched_path):
-                    yield iiobj, iilookup_list, iimatched_path
+                yield from get_from_zero_or_more(iobj, ilookup_list, imatched_path)
 
         prevent_doubles = set()
         for iobj, ilookup_list, imatched_path in get_from_zero_or_more(
-                obj, lookup_list, matched_path, first_element):
+            obj, lookup_list, matched_path, first_element
+        ):
             if isinstance(iobj, Postponed):
                 yield iobj, ilookup_list, imatched_path
                 return
@@ -368,19 +393,21 @@ def get_from_zero_or_more(obj, lookup_list, matched_path,
 
 class RRELPath(RRELBase):
     def __init__(self, path_elements):
-        super(RRELPath, self).__init__()
+        super().__init__()
         # print("create Path :" + str(path_elements))
         self.path_elements = path_elements
-        if (self.path_elements[0] == '^'):
-            self.path_elements[0] = RRELZeroOrMore(RRELBrackets(
-                RRELSequence([RRELPath([RRELDots(2)])])))
+        if self.path_elements[0] == "^":
+            self.path_elements[0] = RRELZeroOrMore(
+                RRELBrackets(RRELSequence([RRELPath([RRELDots(2)])]))
+            )
 
     def __repr__(self):
         if isinstance(self.path_elements[0], RRELDots):
-            return str(self.path_elements[0]) + '.'.join(
-                map(lambda x: str(x), self.path_elements[1:]))
+            return str(self.path_elements[0]) + ".".join(
+                map(lambda x: str(x), self.path_elements[1:])
+            )
         else:
-            return '.'.join(map(lambda x: str(x), self.path_elements))
+            return ".".join(map(lambda x: str(x), self.path_elements))
 
     def start_locally(self):
         return self.path_elements[0].start_locally()
@@ -388,39 +415,45 @@ def start_locally(self):
     def start_at_root(self):
         return self.path_elements[0].start_at_root()
 
-    def get_next_matches(self, obj, lookup_list, allowed, matched_path,
-                         first_element=False):
+    def get_next_matches(
+        self, obj, lookup_list, allowed, matched_path, first_element=False
+    ):
         from textx.scoping import Postponed
 
-        def intern_get_next_matches(obj, lookup_list, allowed, matched_path,
-                                    first_element=False, idx=0):
+        def intern_get_next_matches(
+            obj, lookup_list, allowed, matched_path, first_element=False, idx=0
+        ):
             assert len(self.path_elements) > idx
             e = self.path_elements[idx]
             for iobj, ilookup_list, imatched_path in e.get_next_matches(
-                    obj, lookup_list, allowed, matched_path, first_element):
+                obj, lookup_list, allowed, matched_path, first_element
+            ):
                 if isinstance(iobj, Postponed):
                     yield iobj, ilookup_list, imatched_path
                     return
                 if len(self.path_elements) - 1 == idx:
                     yield iobj, ilookup_list, imatched_path
                 else:
-                    for iiobj, iilookup_list, \
-                        iimatched_path in intern_get_next_matches(
-                            iobj, ilookup_list, allowed, imatched_path,
-                            first_element=False, idx=idx + 1):
-                        yield iiobj, iilookup_list, iimatched_path
+                    yield from intern_get_next_matches(
+                        iobj,
+                        ilookup_list,
+                        allowed,
+                        imatched_path,
+                        first_element=False,
+                        idx=idx + 1,
+                    )
 
-        for iobj, ilookup_list, imatched_path in intern_get_next_matches(
-                obj, lookup_list, allowed, matched_path, first_element, 0):
-            yield iobj, ilookup_list, imatched_path
+        yield from intern_get_next_matches(
+            obj, lookup_list, allowed, matched_path, first_element, 0
+        )
 
 
 class RRELExpression:
     def __init__(self, seq, flags):
         self.seq = seq
         self.flags = flags
-        self.importURI = ('m' in flags)
-        self.use_proxy = ('p' in flags)
+        self.importURI = "m" in flags
+        self.use_proxy = "p" in flags
 
         def prepare_tree(node):
             if isinstance(node, RRELNavigation):
@@ -432,6 +465,7 @@ def prepare_tree(node):
                             prepare_tree(e)
                     else:
                         prepare_tree(c)
+
         prepare_tree(self.seq)
 
     def __repr__(self):
@@ -442,13 +476,12 @@ def __repr__(self):
 
 
 class RRELVisitor(PTNodeVisitor):
-
     def visit_rrel_parent(self, node, children):
         return RRELParent(children[0])
 
     def visit_rrel_navigation(self, node, children):
         if len(children) == 2:
-            if 'string_value' in children.results:
+            if "string_value" in children.results:
                 return RRELNavigation(children[1], False, children[0])
             else:
                 return RRELNavigation(children[1], False, None)
@@ -486,7 +519,7 @@ def visit_string_value(self, node, children):
         return node.value[1:-1]
 
 
-class ReferenceProxy(object):
+class ReferenceProxy:
     """
     This class describes a resolved reference of an RREL
     with 'p'-flag set ('+p:'). It can be used similar to
@@ -503,30 +536,31 @@ class ReferenceProxy(object):
        describing the path of named objects traversed
        during reference resolution.
     """
+
     def __init__(self, path):
         assert len(path) > 0
-        self.__dict__['_tx_path'] = path
+        self.__dict__["_tx_path"] = path
 
     def __setattr__(self, key, value):
-        if key == '_tx_path' or key == '_tx_obj':
+        if key == "_tx_path" or key == "_tx_obj":
             raise Exception("not allowed to set _tx_path or _tx_obj")
-        return setattr(self.__dict__['_tx_path'][-1], key, value)
+        return setattr(self.__dict__["_tx_path"][-1], key, value)
 
     def __getattr__(self, item):
-        if item == '_tx_path':
-            return self.__dict__['_tx_path']
-        elif item == '_tx_obj':
-            return self.__dict__['_tx_path'][-1]
+        if item == "_tx_path":
+            return self.__dict__["_tx_path"]
+        elif item == "_tx_obj":
+            return self.__dict__["_tx_path"][-1]
         else:
-            return getattr(self.__dict__['_tx_path'][-1], item)
+            return getattr(self.__dict__["_tx_path"][-1], item)
 
     def __delattr__(self, item):
-        if item == '_tx_path' or item == '_tx_obj':
+        if item == "_tx_path" or item == "_tx_obj":
             raise Exception("not allowed to del _tx_path or _tx_obj")
-        return delattr(self.__dict__['_tx_path'][-1], item)
+        return delattr(self.__dict__["_tx_path"][-1], item)
 
     def __eq__(self, other):
-        return self.__dict__['_tx_path'][-1] == other
+        return self.__dict__["_tx_path"][-1] == other
 
 
 def parse(rrel_expression):
@@ -540,13 +574,13 @@ def parse(rrel_expression):
         A RREL expression tree.
     """
     from arpeggio import ParserPython
+
     parser = ParserPython(rrel_standalone, reduce_tree=False)
     parse_tree = parser.parse(rrel_expression)
     return visit_parse_tree(parse_tree, RRELVisitor())
 
 
-def find_object_with_path(obj, lookup_list, rrel_tree, obj_cls=None,
-                          split_string="."):
+def find_object_with_path(obj, lookup_list, rrel_tree, obj_cls=None, split_string="."):
     from textx import textx_isinstance
     from textx.scoping import Postponed
 
@@ -569,17 +603,18 @@ def allowed(obj, lookup_list, e):
 
     for p in rrel_tree.paths:
         for obj_res, lookup_list_res, matched_path in p.get_next_matches(
-                obj, lookup_list, allowed, [], first_element=True):
+            obj, lookup_list, allowed, [], first_element=True
+        ):
             if isinstance(obj_res, Postponed):
                 return obj_res  # Postponed
-            elif len(lookup_list_res) == 0:
-                if obj_cls is None or textx_isinstance(obj_res, obj_cls):
-                    return obj_res, matched_path  # found match
+            elif len(lookup_list_res) == 0 and (
+                obj_cls is None or textx_isinstance(obj_res, obj_cls)
+            ):
+                return obj_res, matched_path  # found match
     return None  # not found
 
 
-def find(obj, lookup_list, rrel_tree, obj_cls=None, split_string=".",
-         use_proxy=False):
+def find(obj, lookup_list, rrel_tree, obj_cls=None, split_string=".", use_proxy=False):
     """
     This function gets all/one element from a model
     object based on an rrel tree (query).
@@ -626,10 +661,11 @@ def create_rrel_scope_provider(rrel_tree_or_string, split_string=None, **kwargs)
     """
     from textx.scoping.providers import ImportURI
 
-    class RREL(object):
+    class RREL:
         """
         RREL scope provider
         """
+
         def __init__(self, rrel_tree, split_string, use_proxy):
             """
             Creates a RREL scope provider
@@ -660,30 +696,49 @@ def __call__(self, current_obj, attr, obj_ref):
 
             if self.split_string is None:
                 from textx import get_metamodel
+
                 rule = get_metamodel(current_obj)[obj_ref.match_rule_name]
-                if hasattr(rule._tx_peg_rule, 'split'):
+                if hasattr(rule._tx_peg_rule, "split"):
                     split = rule._tx_peg_rule.split
                 else:
-                    split = '.'
+                    split = "."
             else:
                 split = self.split_string
 
-            return find(current_obj, obj_name, self.rrel_tree, obj_cls,
-                        split_string=split, use_proxy=self.use_proxy)
+            return find(
+                current_obj,
+                obj_name,
+                self.rrel_tree,
+                obj_cls,
+                split_string=split,
+                use_proxy=self.use_proxy,
+            )
 
     class RRELImportURI(ImportURI):
         """
         scope provider with ImportURI and RREL
         """
 
-        def __init__(self, rrel_tree, split_string, use_proxy,
-                     glob_args=None, search_path=None, importAs=False,
-                     importURI_converter=None, importURI_to_scope_name=None):
-            ImportURI.__init__(self, RREL(rrel_tree, split_string, use_proxy),
-                               glob_args=glob_args,
-                               search_path=search_path, importAs=importAs,
-                               importURI_converter=importURI_converter,
-                               importURI_to_scope_name=importURI_to_scope_name)
+        def __init__(
+            self,
+            rrel_tree,
+            split_string,
+            use_proxy,
+            glob_args=None,
+            search_path=None,
+            importAs=False,
+            importURI_converter=None,
+            importURI_to_scope_name=None,
+        ):
+            ImportURI.__init__(
+                self,
+                RREL(rrel_tree, split_string, use_proxy),
+                glob_args=glob_args,
+                search_path=search_path,
+                importAs=importAs,
+                importURI_converter=importURI_converter,
+                importURI_to_scope_name=importURI_to_scope_name,
+            )
 
         def __call__(self, obj, attr, obj_ref):
             # override `__call__`in order to ignore the `default ImportURI`
@@ -696,8 +751,8 @@ def __call__(self, obj, attr, obj_ref):
         rrel_tree_or_string = parse(rrel_tree_or_string)
 
     if rrel_tree_or_string.importURI:
-        return RRELImportURI(rrel_tree_or_string, split_string,
-                             rrel_tree_or_string.use_proxy, *kwargs)
+        return RRELImportURI(
+            rrel_tree_or_string, split_string, rrel_tree_or_string.use_proxy, *kwargs
+        )
     else:
-        return RREL(rrel_tree_or_string, split_string,
-                    rrel_tree_or_string.use_proxy)
+        return RREL(rrel_tree_or_string, split_string, rrel_tree_or_string.use_proxy)
diff --git a/textx/scoping/tools.py b/textx/scoping/tools.py
index 98ee0da1b..c5a763131 100644
--- a/textx/scoping/tools.py
+++ b/textx/scoping/tools.py
@@ -4,9 +4,10 @@
 # Author: Pierre Bayerl
 # License: MIT License
 #######################################################################
-from textx import get_children, get_model
 import re
 
+from textx import get_children, get_model
+
 
 def needs_to_be_resolved(parent_obj, attr_name):
     """
@@ -26,8 +27,9 @@ def needs_to_be_resolved(parent_obj, attr_name):
 
     """
     if hasattr(get_model(parent_obj), "_tx_reference_resolver"):
-        return get_model(parent_obj)._tx_reference_resolver. \
-            has_unresolved_crossrefs(parent_obj, attr_name)
+        return get_model(parent_obj)._tx_reference_resolver.has_unresolved_crossrefs(
+            parent_obj, attr_name
+        )
     else:
         return False
 
@@ -52,11 +54,12 @@ def get_list_of_concatenated_objects(def_obj, path_to_extension):
         entry is returned).
     """
     from textx.scoping import Postponed
+
     def_objs = []
     assert def_obj is not None
 
     def rec_walk(obj_or_list):
-        if (obj_or_list is not None):
+        if obj_or_list is not None:
             if not isinstance(obj_or_list, list):
                 obj_or_list = [obj_or_list]
             for o in obj_or_list:
@@ -82,8 +85,7 @@ def get_parser(model_obj):
 
 
 def get_recursive_parent_with_typename(obj, desired_parent_typename):
-    while type(obj).__name__ != desired_parent_typename and hasattr(obj,
-                                                                    "parent"):
+    while type(obj).__name__ != desired_parent_typename and hasattr(obj, "parent"):
         obj = obj.parent
     if type(obj).__name__ != desired_parent_typename:
         return None
@@ -109,8 +111,7 @@ def get_named_obj_in_list(obj_list, name):
         return None
 
 
-def resolve_model_path(obj, dot_separated_name,
-                       follow_named_element_in_lists=False):
+def resolve_model_path(obj, dot_separated_name, follow_named_element_in_lists=False):
     """
     Get a model object based on a model-path starting from some
     model object. It can be used in the same way you would
@@ -141,29 +142,29 @@ def resolve_model_path(obj, dot_separated_name,
         refs are found on the path / or obj is not found
     """
     from textx.scoping import Postponed
+
     names = dot_separated_name.split(".")
-    match = re.match(r'parent\((\w+)\)', names[0])
+    match = re.match(r"parent\((\w+)\)", names[0])
 
     if obj is None or type(obj) is Postponed:
         return obj
-    elif type(obj) is list:
+    elif isinstance(obj, list):
         if follow_named_element_in_lists:
             next_obj = get_named_obj_in_list(obj, names[0])
         else:
             from textx.exceptions import TextXError
-            raise TextXError(
-                "unexpected: got list in path for get_referenced_object")
+
+            raise TextXError("unexpected: got list in path for get_referenced_object")
     elif match:
         next_obj = obj
         desired_parent_typename = match.group(1)
-        next_obj = get_recursive_parent_with_typename(
-            next_obj,
-            desired_parent_typename)
+        next_obj = get_recursive_parent_with_typename(next_obj, desired_parent_typename)
         if type(next_obj) is Postponed:
             return next_obj
         elif next_obj is not None:
-            return resolve_model_path(next_obj, ".".join(names[1:]),
-                                      follow_named_element_in_lists)
+            return resolve_model_path(
+                next_obj, ".".join(names[1:]), follow_named_element_in_lists
+            )
         else:
             return None
     else:
@@ -173,8 +174,9 @@ def resolve_model_path(obj, dot_separated_name,
         elif next_obj is None:
             return None
     if len(names) > 1:
-        return resolve_model_path(next_obj, ".".join(
-            names[1:]), follow_named_element_in_lists)
+        return resolve_model_path(
+            next_obj, ".".join(names[1:]), follow_named_element_in_lists
+        )
     return next_obj
 
 
@@ -190,7 +192,7 @@ def get_unique_named_object_in_all_models(root, name):
     Returns:
         the object (if not unique, raises an error)
     """
-    if hasattr(root, '_tx_model_repository'):
+    if hasattr(root, "_tx_model_repository"):
         src = list(root._tx_model_repository.local_models)
         if root not in src:
             src.append(root)
@@ -200,8 +202,7 @@ def get_unique_named_object_in_all_models(root, name):
     a = []
     for m in src:
         # print("analyzing {}".format(m._tx_filename))
-        a = a + get_children(
-            lambda x: hasattr(x, 'name') and x.name == name, m)
+        a = a + get_children(lambda x: hasattr(x, "name") and x.name == name, m)
 
     assert len(a) == 1
     return a[0]
@@ -218,7 +219,7 @@ def get_unique_named_object(root, name):
     Returns:
         the object (if not unique, raises an error)
     """
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == name, root)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == name, root)
     assert len(a) == 1
     return a[0]
 


Test patch:
diff --git a/examples/Entity/entity_test.py b/examples/Entity/entity_test.py
index 6a16752cd..e1e2a6ea7 100644
--- a/examples/Entity/entity_test.py
+++ b/examples/Entity/entity_test.py
@@ -1,14 +1,13 @@
-from __future__ import unicode_literals
 import os
 from os.path import dirname, join
+
 from textx import metamodel_from_file
 from textx.export import metamodel_export, model_export
 
-
 this_folder = dirname(__file__)
 
 
-class SimpleType(object):
+class SimpleType:
     """
     We are registering user SimpleType class to support
     simple types (integer, string) in our entity models
diff --git a/runtests.sh b/runtests.sh
index 202e90a09..e1d838ea8 100755
--- a/runtests.sh
+++ b/runtests.sh
@@ -5,4 +5,4 @@ coverage run --source textx -m pytest tests/functional || exit 1
 coverage report --fail-under 90 || exit 1
 # Run this to generate html report
 # coverage html --directory=coverage
-flake8 || exit 1
+ruff check textx/ tests/ examples/ || exit 1
diff --git a/tests/functional/examples/test_free_text_with_references.py b/tests/functional/examples/test_free_text_with_references.py
index 383dee35b..65bc0ab75 100644
--- a/tests/functional/examples/test_free_text_with_references.py
+++ b/tests/functional/examples/test_free_text_with_references.py
@@ -1,16 +1,9 @@
 """
 Testing model and regexp with groups.
 """
-from __future__ import unicode_literals
 import pytest  # noqa
-import sys
 from textx import metamodel_from_str
 
-if sys.version < '3':
-    text = unicode  # noqa
-else:
-    text = str
-
 grammar = r'''
 Model: entries += Entry;
 
@@ -37,9 +30,9 @@
 '''
 
 
-class Entry(object):
+class Entry:
     def __init__(self, **kwargs):
-        for k in kwargs.keys():
+        for k in kwargs:
             setattr(self, k, kwargs[k])
 
     def __str__(self):
@@ -47,7 +40,7 @@ def __str__(self):
         text = ""
         for d in self.data:
             text += d.text
-            text += "@[{}]".format(d.ref.name)
+            text += f"@[{d.ref.name}]"
         text += self.datalast.text
         return text.replace(r"\@", "@")
 
@@ -62,23 +55,22 @@ def test_free_text_with_references():
     ENTRY NoLink:   """Just text"""
     ENTRY Empty:    """"""
     '''  # noqa
-    metamodel = metamodel_from_str(grammar, classes=[Entry],
-                                   use_regexp_group=True)
+    metamodel = metamodel_from_str(grammar, classes=[Entry], use_regexp_group=True)
     m = metamodel.model_from_str(model_str)
 
-    assert 1 == len(m.entries[0].data)
-    assert 1 == len(m.entries[1].data)
-    assert 5 == len(m.entries[2].data)
-    assert 1 == len(m.entries[3].data)
-    assert 2 == len(m.entries[4].data)
-    assert 0 == len(m.entries[5].data)
-    assert 0 == len(m.entries[6].data)
+    assert len(m.entries[0].data) == 1
+    assert len(m.entries[1].data) == 1
+    assert len(m.entries[2].data) == 5
+    assert len(m.entries[3].data) == 1
+    assert len(m.entries[4].data) == 2
+    assert len(m.entries[5].data) == 0
+    assert len(m.entries[6].data) == 0
 
-    assert 'Hi' == m.entries[0].data[0].ref.name
+    assert m.entries[0].data[0].ref.name == "Hi"
     assert m.entries[1] == m.entries[0].data[0].ref
 
-    assert 'a way to say hello@mail (see @[Hi])' == str(m.entries[0])
-    assert 'german way to say hello (see ""@[Hello]"")' == str(m.entries[3])
-    assert 'another french "@@[Hello]", see @[Salut]' == str(m.entries[4])
-    assert 'Just text' == str(m.entries[5])
-    assert '' == str(m.entries[6])
+    assert str(m.entries[0]) == "a way to say hello@mail (see @[Hi])"
+    assert str(m.entries[3]) == 'german way to say hello (see ""@[Hello]"")'
+    assert str(m.entries[4]) == 'another french "@@[Hello]", see @[Salut]'
+    assert str(m.entries[5]) == "Just text"
+    assert str(m.entries[6]) == ""
diff --git a/tests/functional/examples/test_hierarchical_data_structures_referencing_attributes.py b/tests/functional/examples/test_hierarchical_data_structures_referencing_attributes.py
index 82509be0c..5dc57290a 100644
--- a/tests/functional/examples/test_hierarchical_data_structures_referencing_attributes.py
+++ b/tests/functional/examples/test_hierarchical_data_structures_referencing_attributes.py
@@ -1,31 +1,31 @@
-from __future__ import unicode_literals
-from textx import metamodel_from_str
+import attr
 from pytest import raises
+
 import textx.exceptions
-import attr
+from textx import metamodel_from_str
 
 
 @attr.s(frozen=True)
-class Instance(object):
+class Instance:
     parent = attr.ib()
     name = attr.ib()
     type = attr.ib()
 
 
 @attr.s(frozen=True)
-class Reference(object):
+class Reference:
     parent = attr.ib()
     instance = attr.ib()
     refs = attr.ib()
 
 
 @attr.s(frozen=True)
-class RefItem(object):
+class RefItem:
     parent = attr.ib()
     valref = attr.ib()
 
 
-model_text = '''
+model_text = """
     struct A {
         val x
     }
@@ -46,7 +46,7 @@ class RefItem(object):
     reference d.c.b.a.x
     reference d.b1.a.x
     reference a.x
-'''
+"""
 
 
 def test_referencing_attributes():
@@ -60,7 +60,7 @@ def test_referencing_attributes():
     "RefItem"s, can the be resolved on after the other...
     We also show how to handle custom classes here.
     """
-    grammar = '''
+    grammar = """
     Model:
         structs+=Struct
         instances+=Instance
@@ -75,62 +75,59 @@ def test_referencing_attributes():
         'reference' instance=[Instance] refs+=RefItem;
     RefItem:
         '.' valref=[Val];
-    '''
+    """
 
     for classes in [[], [Instance, Reference, RefItem]]:
 
         def ref_scope(refItem, myattr, attr_ref):
-            from textx.scoping.tools import get_named_obj_in_list
-            from textx.scoping import Postponed
             from textx import textx_isinstance
+            from textx.scoping import Postponed
+            from textx.scoping.tools import get_named_obj_in_list
 
             reference = refItem.parent
 
             if reference is None:
                 return Postponed()
 
-            index = list(map(
-                lambda x: id(x), reference.refs)).index(id(refItem))
+            index = list(map(lambda x: id(x), reference.refs)).index(id(refItem))
 
             assert index >= 0
 
-            base = reference.instance if index == 0 \
-                else reference.refs[index - 1].valref
+            base = reference.instance if index == 0 else reference.refs[index - 1].valref
             if base is None or base.type is None:
                 return Postponed()
             x = get_named_obj_in_list(base.type.vals, attr_ref.obj_name)
-            if index == len(reference.refs) - 1:
-                if not textx_isinstance(x, myattr.cls):
-                    print(x)
-                    return None
+            if index == len(reference.refs) - 1 and not textx_isinstance(x, myattr.cls):
+                print(x)
+                return None
 
             return x
 
         mm = metamodel_from_str(grammar, classes=classes)
-        mm.register_scope_providers({
-            "RefItem.valref": ref_scope
-        })
+        mm.register_scope_providers({"RefItem.valref": ref_scope})
         m = mm.model_from_str(model_text)
 
-        assert m.references[0].refs[-1].valref.name == 'x'
+        assert m.references[0].refs[-1].valref.name == "x"
         assert m.references[0].refs[-1].valref == m.structs[0].vals[0]
 
-        assert m.references[0].refs[-2].valref.name == 'a'
+        assert m.references[0].refs[-2].valref.name == "a"
         assert m.references[0].refs[-2].valref == m.structs[1].vals[0]
 
-        assert m.references[0].refs[-3].valref.name == 'b'
+        assert m.references[0].refs[-3].valref.name == "b"
         assert m.references[0].refs[-3].valref == m.structs[2].vals[0]
 
         assert m.references[1].refs[-1].valref == m.structs[0].vals[0]
 
-        assert m.references[2].refs[0].valref.name == 'x'
+        assert m.references[2].refs[0].valref.name == "x"
         assert m.references[2].refs[0].valref == m.structs[0].vals[0]
 
         # negative tests
         # error: "not_there" not part of A
-        with raises(textx.exceptions.TextXSemanticError,
-                    match=r'.*Unknown object.*not_there.*'):
-            mm.model_from_str('''
+        with raises(
+            textx.exceptions.TextXSemanticError, match=r".*Unknown object.*not_there.*"
+        ):
+            mm.model_from_str(
+                """
             struct A { val x }
             struct B { val a: A}
             struct C {
@@ -139,12 +136,16 @@ def ref_scope(refItem, myattr, attr_ref):
             }
             instance c: C
             reference c.b.a.not_there
-            ''')
+            """
+            )
 
         # error: B.a is not of type A
-        with raises(textx.exceptions.TextXSemanticError,
-                    match=r'.*Unresolvable cross references.*x.*'):
-            mm.model_from_str('''
+        with raises(
+            textx.exceptions.TextXSemanticError,
+            match=r".*Unresolvable cross references.*x.*",
+        ):
+            mm.model_from_str(
+                """
             struct A { val x }
             struct B { val a }
             struct C {
@@ -153,7 +154,8 @@ def ref_scope(refItem, myattr, attr_ref):
             }
             instance c: C
             reference c.b.a.x
-            ''')
+            """
+            )
 
 
 def test_referencing_attributes_with_rrel_all_in_one():
@@ -161,7 +163,8 @@ def test_referencing_attributes_with_rrel_all_in_one():
     RREL solution: all scope provider information encoded in the grammar.
     """
 
-    mm = metamodel_from_str('''
+    mm = metamodel_from_str(
+        """
         Model:
             structs+=Struct
             instances+=Instance
@@ -175,23 +178,26 @@ def test_referencing_attributes_with_rrel_all_in_one():
         Reference:
             'reference' ref=[Val:FQN|instances.~type.vals.(~type.vals)*];
         FQN: ID ('.' ID)*;
-        ''')
+        """
+    )
     m = mm.model_from_str(model_text)
-    m.references[-1].ref == m.structs[0].vals[0]  # a.x
+    assert m.references[-1].ref == m.structs[0].vals[0]  # a.x
 
-    assert m.references[0].ref.name == 'x'
+    assert m.references[0].ref.name == "x"
     assert m.references[0].ref == m.structs[0].vals[0]
 
     assert m.references[1].ref == m.structs[0].vals[0]
 
-    assert m.references[2].ref.name == 'x'
+    assert m.references[2].ref.name == "x"
     assert m.references[2].ref == m.structs[0].vals[0]
 
     # negative tests
     # error: "not_there" not part of A
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*Unknown object "c.b.a.not_there".*'):
-        mm.model_from_str('''
+    with raises(
+        textx.exceptions.TextXSemanticError, match=r'.*Unknown object "c.b.a.not_there".*'
+    ):
+        mm.model_from_str(
+            """
         struct A { val x }
         struct B { val a: A}
         struct C {
@@ -200,12 +206,15 @@ def test_referencing_attributes_with_rrel_all_in_one():
         }
         instance c: C
         reference c.b.a.not_there
-        ''')
+        """
+        )
 
     # error: B.a is not of type A
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*Unknown object "c.b.a.x".*'):
-        mm.model_from_str('''
+    with raises(
+        textx.exceptions.TextXSemanticError, match=r'.*Unknown object "c.b.a.x".*'
+    ):
+        mm.model_from_str(
+            """
         struct A { val x }
         struct B { val a }
         struct C {
@@ -214,7 +223,8 @@ def test_referencing_attributes_with_rrel_all_in_one():
         }
         instance c: C
         reference c.b.a.x
-        ''')
+        """
+        )
 
 
 def test_referencing_attributes_with_rrel_all_in_one_splitstring():
@@ -222,7 +232,8 @@ def test_referencing_attributes_with_rrel_all_in_one_splitstring():
     RREL solution: variation with diffferent split string specified in match rule.
     """
 
-    mm = metamodel_from_str('''
+    mm = metamodel_from_str(
+        """
         Model:
             structs+=Struct
             instances+=Instance
@@ -237,8 +248,10 @@ def test_referencing_attributes_with_rrel_all_in_one_splitstring():
             'reference' instance=[Instance]
             '.' ref=[Val:FQN|.~instance.~type.vals.(~type.vals)*];
         FQN[split='->']: ID ('->' ID)*;
-        ''')
-    m = mm.model_from_str('''
+        """
+    )
+    m = mm.model_from_str(
+        """
         struct A {
             val x
         }
@@ -259,14 +272,15 @@ def test_referencing_attributes_with_rrel_all_in_one_splitstring():
         reference d.c->b->a->x
         reference d.b1->a->x
         reference a.x
-    ''')
+    """
+    )
 
-    assert m.references[0].ref.name == 'x'
+    assert m.references[0].ref.name == "x"
     assert m.references[0].ref == m.structs[0].vals[0]
 
     assert m.references[1].ref == m.structs[0].vals[0]
 
-    assert m.references[2].ref.name == 'x'
+    assert m.references[2].ref.name == "x"
     assert m.references[2].ref == m.structs[0].vals[0]
 
 
@@ -275,7 +289,8 @@ def test_referencing_attributes_with_rrel_and_full_path_access():
     RREL solution: all scope provider information encoded in the grammar.
     """
 
-    mm = metamodel_from_str('''
+    mm = metamodel_from_str(
+        """
         Model:
             structs+=Struct
             instances+=Instance
@@ -289,19 +304,20 @@ def test_referencing_attributes_with_rrel_and_full_path_access():
         Reference:
             'reference' ref=[Val:FQN|+p:instances.~type.vals.(~type.vals)*];
         FQN: ID ('.' ID)*;
-        ''')
+        """
+    )
     m = mm.model_from_str(model_text)
 
-    assert m.references[0].ref.name == 'x'
+    assert m.references[0].ref.name == "x"
     assert m.references[0].ref._tx_obj is m.structs[0].vals[0]
     assert m.references[0].ref == m.structs[0].vals[0]
-    assert not m.references[0].ref is m.structs[0].vals[0]
-    assert textx.textx_isinstance(m.references[0].ref, mm['Val'])
-    assert not textx.textx_isinstance(m.references[0].ref, mm['Struct'])
+    assert m.references[0].ref is not m.structs[0].vals[0]
+    assert textx.textx_isinstance(m.references[0].ref, mm["Val"])
+    assert not textx.textx_isinstance(m.references[0].ref, mm["Struct"])
 
     assert m.references[1].ref == m.structs[0].vals[0]
 
-    assert m.references[2].ref.name == 'x'
+    assert m.references[2].ref.name == "x"
     assert m.references[2].ref == m.structs[0].vals[0]
 
     # (allows to access all intermediate referenced named
@@ -309,20 +325,20 @@ def test_referencing_attributes_with_rrel_and_full_path_access():
     objpath = m.references[0].ref._tx_path
 
     assert objpath[0] == m.instances[0]
-    assert objpath[0].name == 'd'
-    assert objpath[1].name == 'c'
-    assert objpath[2].name == 'b'
-    assert objpath[3].name == 'a'
-    assert objpath[4].name == 'x'
-
-    setattr(m.references[0].ref, "extra", "ok to add extra field")
-    with raises(Exception, match=r'.*not allowed.*'):
-        setattr(m.references[0].ref, "_tx_obj", "not ok")
-    with raises(Exception, match=r'.*not allowed.*'):
-        setattr(m.references[0].ref, "_tx_path", "not ok")
+    assert objpath[0].name == "d"
+    assert objpath[1].name == "c"
+    assert objpath[2].name == "b"
+    assert objpath[3].name == "a"
+    assert objpath[4].name == "x"
+
+    m.references[0].ref.extra = "ok to add extra field"
+    with raises(Exception, match=r".*not allowed.*"):
+        m.references[0].ref._tx_obj = "not ok"
+    with raises(Exception, match=r".*not allowed.*"):
+        m.references[0].ref._tx_path = "not ok"
 
     del m.references[0].ref.extra
-    with raises(Exception, match=r'.*not allowed.*'):
+    with raises(Exception, match=r".*not allowed.*"):
         del m.references[0].ref._tx_obj
-    with raises(Exception, match=r'.*not allowed.*'):
+    with raises(Exception, match=r".*not allowed.*"):
         del m.references[0].ref._tx_path
diff --git a/tests/functional/examples/test_modeling_float_int_variables.py b/tests/functional/examples/test_modeling_float_int_variables.py
index d8079ad98..c691214cf 100644
--- a/tests/functional/examples/test_modeling_float_int_variables.py
+++ b/tests/functional/examples/test_modeling_float_int_variables.py
@@ -1,9 +1,8 @@
-from __future__ import unicode_literals
 from textx import metamodel_from_str
 
 
 def test_int_and_floats():
-    grammar = r'''
+    grammar = r"""
     Model: vars+=Var;
     Var: name=ID '=' value=Val;
     Val: O_FLOAT|O_INT; // order matters: float first!
@@ -11,30 +10,27 @@ def test_int_and_floats():
     O_FLOAT: floatVal=MYFLOAT;
     MYINT: /\-?\d+/;
     MYFLOAT: /\-?\d+.\d+([eE][-+]?\d+)?/;
-    '''
-    model_text = '''
+    """
+    model_text = """
     x1 = 1
     x2 = -1
     y1 = 1.0
     y2 = 1.1e-2
     y3 = -1.1e+2
-    '''
+    """
 
     mm = metamodel_from_str(grammar)
-    mm.register_obj_processors({
-        "MYINT": lambda x: int(x),
-        "MYFLOAT": lambda x: float(x)
-    })
+    mm.register_obj_processors({"MYINT": lambda x: int(x), "MYFLOAT": lambda x: float(x)})
     m = mm.model_from_str(model_text)
 
-    assert 'O_INT' == m.vars[0].value.__class__.__name__
-    assert 'O_INT' == m.vars[1].value.__class__.__name__
-    assert 'O_FLOAT' == m.vars[2].value.__class__.__name__
-    assert 'O_FLOAT' == m.vars[3].value.__class__.__name__
-    assert 'O_FLOAT' == m.vars[4].value.__class__.__name__
+    assert m.vars[0].value.__class__.__name__ == "O_INT"
+    assert m.vars[1].value.__class__.__name__ == "O_INT"
+    assert m.vars[2].value.__class__.__name__ == "O_FLOAT"
+    assert m.vars[3].value.__class__.__name__ == "O_FLOAT"
+    assert m.vars[4].value.__class__.__name__ == "O_FLOAT"
 
-    assert 1 == m.vars[0].value.intVal
-    assert -1 == m.vars[1].value.intVal
+    assert m.vars[0].value.intVal == 1
+    assert m.vars[1].value.intVal == -1
     assert abs(1.0 - m.vars[2].value.floatVal) < 1e-5
     assert abs(1.1e-2 - m.vars[3].value.floatVal) < 1e-5
-    assert abs(-1.1e+2 - m.vars[4].value.floatVal) < 1e-5
+    assert abs(-1.1e2 - m.vars[4].value.floatVal) < 1e-5
diff --git a/tests/functional/examples/test_reference_to_python.py b/tests/functional/examples/test_reference_to_python.py
index 08fc19476..50186ac44 100644
--- a/tests/functional/examples/test_reference_to_python.py
+++ b/tests/functional/examples/test_reference_to_python.py
@@ -1,9 +1,9 @@
-from __future__ import unicode_literals
-from textx import metamodel_from_str
 from pytest import raises
+
+from textx import metamodel_from_str
 from textx.scoping.tools import get_unique_named_object
 
-metamodel_str = r'''
+metamodel_str = r"""
 Model:
     access+=Access
 ;
@@ -12,7 +12,7 @@
 ;
 
 Comment: /\/\/.*$/;
-'''
+"""
 
 
 class PythonScopeProvider:
@@ -24,15 +24,16 @@ def __call__(self, obj, attr, attr_ref):
             if attr_ref.obj_name in self.dict_with_objects:
                 return self.dict_with_objects[attr_ref.obj_name]
             else:
-                raise Exception("{} not found".format(attr_ref.obj_name))
+                raise Exception(f"{attr_ref.obj_name} not found")
         else:
             if not obj.pyobj:
                 from textx.scoping import Postponed
+
                 return Postponed()
             if hasattr(obj.pyobj, attr_ref.obj_name):
                 return getattr(obj.pyobj, attr_ref.obj_name)
             else:
-                raise Exception("{} not found".format(attr_ref.obj_name))
+                raise Exception(f"{attr_ref.obj_name} not found")
 
 
 def test_reference_to_python_attribute():
@@ -41,28 +42,25 @@ def test_reference_to_python_attribute():
     # "access" objects access python attributes.
 
     from collections import namedtuple
-    Person = namedtuple('Person', 'first_name last_name zip_code')
-    p1 = Person('Tim', 'Foo', 123)
-    p2 = Person('Tom', 'Bar', 456)
 
-    sp = PythonScopeProvider({
-        "p1": p1,
-        "p2": p2
-    })
+    Person = namedtuple("Person", "first_name last_name zip_code")
+    p1 = Person("Tim", "Foo", 123)
+    p2 = Person("Tom", "Bar", 456)
+
+    sp = PythonScopeProvider({"p1": p1, "p2": p2})
 
     # create meta model
     my_metamodel = metamodel_from_str(metamodel_str)
-    my_metamodel.register_scope_providers({
-        "Access.pyobj": sp,
-        "Access.pyattr": sp
-    })
+    my_metamodel.register_scope_providers({"Access.pyobj": sp, "Access.pyattr": sp})
 
     # read model
-    my_model = my_metamodel.model_from_str('''
+    my_model = my_metamodel.model_from_str(
+        """
     access A_Tim p1.first_name
     access A_123 p1.zip_code
     access A_456 p2.zip_code
-    ''')
+    """
+    )
 
     # check that the references are OK
     A_Tim = get_unique_named_object(my_model, "A_Tim").pyattr
@@ -72,14 +70,18 @@ def test_reference_to_python_attribute():
     A_456 = get_unique_named_object(my_model, "A_456").pyattr
     assert A_456 == 456
 
-    with raises(Exception, match=r'.*unknown.*'):
-        my_metamodel.model_from_str('''
+    with raises(Exception, match=r".*unknown.*"):
+        my_metamodel.model_from_str(
+            """
         access A1 p1.unknown
-        ''')
+        """
+        )
 
-    with raises(Exception, match=r'.*p3.*'):
-        my_metamodel.model_from_str('''
+    with raises(Exception, match=r".*p3.*"):
+        my_metamodel.model_from_str(
+            """
         access A1 p3.first_anme
-        ''')
+        """
+        )
 
     pass
diff --git a/tests/functional/generators/test_textx_generators.py b/tests/functional/generators/test_textx_generators.py
index edcbd5f59..bb08965a3 100644
--- a/tests/functional/generators/test_textx_generators.py
+++ b/tests/functional/generators/test_textx_generators.py
@@ -2,25 +2,23 @@
 Tests for textX registered generators/visualizations
 """
 import os
+from contextlib import suppress
+
 import pytest
-from textx.cli import textx
 from click.testing import CliRunner
 
+from textx.cli import textx
 
 this_folder = os.path.abspath(os.path.dirname(__file__))
 
 
 @pytest.fixture
 def metamodel_file():
-    try:
-        os.remove(os.path.join(this_folder, 'hello.pu'))
-    except OSError:
-        pass
-    try:
-        os.remove(os.path.join(this_folder, 'hello.dot'))
-    except OSError:
-        pass
-    return os.path.join(this_folder, 'hello.tx')
+    with suppress(OSError):
+        os.remove(os.path.join(this_folder, "hello.pu"))
+    with suppress(OSError):
+        os.remove(os.path.join(this_folder, "hello.dot"))
+    return os.path.join(this_folder, "hello.tx")
 
 
 def test_generate_dot_from_tx(metamodel_file):
@@ -28,14 +26,12 @@ def test_generate_dot_from_tx(metamodel_file):
     Test generating dot from textX grammar.
     """
     runner = CliRunner()
-    result = runner.invoke(textx, ['generate',
-                                   '--target', 'dot',
-                                   metamodel_file])
+    result = runner.invoke(textx, ["generate", "--target", "dot", metamodel_file])
     assert result.exit_code == 0
-    out_file = os.path.join(this_folder, 'hello.dot')
+    out_file = os.path.join(this_folder, "hello.dot")
     assert os.path.exists(out_file)
-    with open(out_file, 'r') as f:
-        assert 'digraph textX' in f.read()
+    with open(out_file) as f:
+        assert "digraph textX" in f.read()
 
 
 def test_generate_plantuml_from_tx(metamodel_file):
@@ -43,14 +39,12 @@ def test_generate_plantuml_from_tx(metamodel_file):
     Test generating pu (PlantUML) file from textX grammar.
     """
     runner = CliRunner()
-    result = runner.invoke(textx, ['generate',
-                                   '--target', 'PlantUML',
-                                   metamodel_file])
+    result = runner.invoke(textx, ["generate", "--target", "PlantUML", metamodel_file])
     assert result.exit_code == 0
-    out_file = os.path.join(this_folder, 'hello.pu')
+    out_file = os.path.join(this_folder, "hello.pu")
     assert os.path.exists(out_file)
-    with open(out_file, 'r') as f:
-        assert '@startuml' in f.read()
+    with open(out_file) as f:
+        assert "@startuml" in f.read()
 
 
 def test_generate_dot_from_any_model(metamodel_file):
@@ -58,13 +52,20 @@ def test_generate_dot_from_any_model(metamodel_file):
     Test generating dot from any textX model.
     """
     runner = CliRunner()
-    result = runner.invoke(textx, ['generate',
-                                   '--target', 'dot',
-                                   '--grammar', metamodel_file,
-                                   os.path.join(this_folder, 'example.hello')])
+    result = runner.invoke(
+        textx,
+        [
+            "generate",
+            "--target",
+            "dot",
+            "--grammar",
+            metamodel_file,
+            os.path.join(this_folder, "example.hello"),
+        ],
+    )
 
     assert result.exit_code == 0
-    hello_file = os.path.join(this_folder, 'example.dot')
+    hello_file = os.path.join(this_folder, "example.dot")
     assert os.path.exists(hello_file)
-    with open(hello_file, 'r') as f:
-        assert 'digraph textX' in f.read()
+    with open(hello_file) as f:
+        assert "digraph textX" in f.read()
diff --git a/tests/functional/registration/projects/data_dsl/data_dsl/__init__.py b/tests/functional/registration/projects/data_dsl/data_dsl/__init__.py
index 3fa57a975..8624b6d65 100644
--- a/tests/functional/registration/projects/data_dsl/data_dsl/__init__.py
+++ b/tests/functional/registration/projects/data_dsl/data_dsl/__init__.py
@@ -1,14 +1,15 @@
 import os
-from textx import metamodel_from_file, language
 
+from textx import language, metamodel_from_file
 
-@language('data-dsl', '*.edata')
+
+@language("data-dsl", "*.edata")
 def data_dsl():
     """
     An example DSL for data definition
     """
     current_dir = os.path.dirname(__file__)
-    p = os.path.join(current_dir, 'Data.tx')
+    p = os.path.join(current_dir, "Data.tx")
     data_mm = metamodel_from_file(p, global_repository=True)
 
     return data_mm
diff --git a/tests/functional/registration/projects/data_dsl/pyproject.toml b/tests/functional/registration/projects/data_dsl/pyproject.toml
new file mode 100644
index 000000000..4db8e9e34
--- /dev/null
+++ b/tests/functional/registration/projects/data_dsl/pyproject.toml
@@ -0,0 +1,11 @@
+[project]
+name = "data_dsl"
+version = "1.0.0"
+
+dependencies = [
+    "textX",
+    "types_dsl"
+]
+
+[project.entry-points.textx_languages]
+data_dsl = "data_dsl:data_dsl"
diff --git a/tests/functional/registration/projects/data_dsl/setup.cfg b/tests/functional/registration/projects/data_dsl/setup.cfg
deleted file mode 100644
index 4b3911d0d..000000000
--- a/tests/functional/registration/projects/data_dsl/setup.cfg
+++ /dev/null
@@ -1,16 +0,0 @@
-[metadata]
-name = data_dsl
-version = 1.0.0
-
-[options]
-packages = find:
-install_requires =
-    textX
-    types_dsl
-
-[options.package_data]
-data_dsl = *.tx
-
-[options.entry_points]
-textx_languages =
-    data_dsl = data_dsl:data_dsl 
\ No newline at end of file
diff --git a/tests/functional/registration/projects/data_dsl/setup.py b/tests/functional/registration/projects/data_dsl/setup.py
deleted file mode 100644
index 606849326..000000000
--- a/tests/functional/registration/projects/data_dsl/setup.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from setuptools import setup
-
-setup()
diff --git a/tests/functional/registration/projects/data_dsl/tests/test_data_dsl.py b/tests/functional/registration/projects/data_dsl/tests/test_data_dsl.py
index 5a8433122..1c044d47c 100644
--- a/tests/functional/registration/projects/data_dsl/tests/test_data_dsl.py
+++ b/tests/functional/registration/projects/data_dsl/tests/test_data_dsl.py
@@ -1,7 +1,7 @@
-from __future__ import unicode_literals
-from textx import metamodel_for_language, clear_language_registrations
 import os
 
+from textx import clear_language_registrations, metamodel_for_language
+
 
 def test_data_dsl():
     """
@@ -10,9 +10,9 @@ def test_data_dsl():
 
     clear_language_registrations()
     current_dir = os.path.dirname(__file__)
-    mmD = metamodel_for_language('data-dsl')
-    model = mmD.model_from_file(os.path.join(current_dir,
-                                             'models',
-                                             'data_structures.edata'))
+    mmD = metamodel_for_language("data-dsl")
+    model = mmD.model_from_file(
+        os.path.join(current_dir, "models", "data_structures.edata")
+    )
     assert model is not None
     assert len(model.data) == 3
diff --git a/tests/functional/registration/projects/flow_codegen/flow_codegen/generators.py b/tests/functional/registration/projects/flow_codegen/flow_codegen/generators.py
index ac6c87d55..55c524171 100644
--- a/tests/functional/registration/projects/flow_codegen/flow_codegen/generators.py
+++ b/tests/functional/registration/projects/flow_codegen/flow_codegen/generators.py
@@ -1,41 +1,42 @@
-import click
 import os
+
+import click
+
 from textx import GeneratorDesc
 
 
-def codegen_flow_pu(metamodel, model, output_path, overwrite, debug=False,
-                    **custom_args):
+def codegen_flow_pu(metamodel, model, output_path, overwrite, debug=False, **custom_args):
     """
     This command transforms *.flow-files to *.pu files (plantuml).
     """
 
     txt = "@startuml\n"
     for a in model.algos:
-        txt += "component {}\n".format(a.name)
+        txt += f"component {a.name}\n"
     for f in model.flows:
-        txt += '{} "{}" #--# {}\n'.format(f.algo1.name, f.algo1.outp.name,
-                                          f.algo2.name)
+        txt += f'{f.algo1.name} "{f.algo1.outp.name}" #--# {f.algo2.name}\n'
     txt += "@enduml\n"
 
     # Dump custom args for testing
-    txt += '\n'.join(["{}={}".format(arg_name, arg_value)
-                      for arg_name, arg_value in custom_args.items()])
+    txt += "\n".join(
+        [f"{arg_name}={arg_value}" for arg_name, arg_value in custom_args.items()]
+    )
 
     input_file = model._tx_filename
     base_dir = output_path if output_path else os.path.dirname(input_file)
     base_name, _ = os.path.splitext(os.path.basename(input_file))
-    output_file = os.path.abspath(
-        os.path.join(base_dir, "{}.{}".format(base_name, 'pu')))
+    output_file = os.path.abspath(os.path.join(base_dir, "{}.{}".format(base_name, "pu")))
     if overwrite or not os.path.exists(output_file):
-        click.echo('-> {}'.format(output_file))
+        click.echo(f"-> {output_file}")
         with open(output_file, "w") as f:
             f.write(txt)
     else:
-        click.echo('-- Skipping: {}'.format(output_file))
+        click.echo(f"-- Skipping: {output_file}")
 
 
 flow_pu = GeneratorDesc(
-    language='flow-dsl',
-    target='PlantUML',
-    description='Generating PlantUML visualization from flow-dsl',
-    generator=codegen_flow_pu)
+    language="flow-dsl",
+    target="PlantUML",
+    description="Generating PlantUML visualization from flow-dsl",
+    generator=codegen_flow_pu,
+)
diff --git a/tests/functional/registration/projects/flow_codegen/pyproject.toml b/tests/functional/registration/projects/flow_codegen/pyproject.toml
new file mode 100644
index 000000000..191a3b2de
--- /dev/null
+++ b/tests/functional/registration/projects/flow_codegen/pyproject.toml
@@ -0,0 +1,11 @@
+[project]
+name = "flow_codegen"
+version = "1.0.0"
+
+dependencies = [
+    "textX",
+    "click"
+]
+
+[project.entry-points.textx_generators]
+flow_dsl_plantuml = "flow_codegen.generators:flow_pu"
diff --git a/tests/functional/registration/projects/flow_codegen/setup.cfg b/tests/functional/registration/projects/flow_codegen/setup.cfg
deleted file mode 100644
index c2f97ac83..000000000
--- a/tests/functional/registration/projects/flow_codegen/setup.cfg
+++ /dev/null
@@ -1,13 +0,0 @@
-[metadata]
-name = flow_codegen
-version = 1.0.0
-
-[options]
-packages = find:
-install_requires =
-    textX
-    click
-
-[options.entry_points]
-textx_generators =
-    flow_dsl_plantuml = flow_codegen.generators:flow_pu
\ No newline at end of file
diff --git a/tests/functional/registration/projects/flow_codegen/setup.py b/tests/functional/registration/projects/flow_codegen/setup.py
deleted file mode 100644
index 606849326..000000000
--- a/tests/functional/registration/projects/flow_codegen/setup.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from setuptools import setup
-
-setup()
diff --git a/tests/functional/registration/projects/flow_dsl/flow_dsl/__init__.py b/tests/functional/registration/projects/flow_dsl/flow_dsl/__init__.py
index a12f8a8fa..7f5206a63 100644
--- a/tests/functional/registration/projects/flow_dsl/flow_dsl/__init__.py
+++ b/tests/functional/registration/projects/flow_dsl/flow_dsl/__init__.py
@@ -1,25 +1,21 @@
 import os
-from textx import metamodel_from_file, language, TextXSemanticError, get_location
 
+from textx import TextXSemanticError, get_location, language, metamodel_from_file
 
-@language('flow-dsl', '*.eflow')
+
+@language("flow-dsl", "*.eflow")
 def flow_dsl():
     """
     An example DSL for data flow processing definition
     """
     current_dir = os.path.dirname(__file__)
-    p = os.path.join(current_dir, 'Flow.tx')
+    p = os.path.join(current_dir, "Flow.tx")
     flow_mm = metamodel_from_file(p, global_repository=True)
 
     def check_flow(f):
         if f.algo1.outp != f.algo2.inp:
-            raise TextXSemanticError(
-                "algo data types must match",
-                **get_location(f)
-            )
+            raise TextXSemanticError("algo data types must match", **get_location(f))
 
-    flow_mm.register_obj_processors({
-        'Flow': check_flow
-    })
+    flow_mm.register_obj_processors({"Flow": check_flow})
 
     return flow_mm
diff --git a/tests/functional/registration/projects/flow_dsl/pyproject.toml b/tests/functional/registration/projects/flow_dsl/pyproject.toml
new file mode 100644
index 000000000..e2ac207c9
--- /dev/null
+++ b/tests/functional/registration/projects/flow_dsl/pyproject.toml
@@ -0,0 +1,10 @@
+[project]
+name = "flow_dsl"
+version = "1.0.0"
+
+dependencies = [
+    "textX"
+]
+
+[project.entry-points.textx_languages]
+flow_dsl = "flow_dsl:flow_dsl"
diff --git a/tests/functional/registration/projects/flow_dsl/setup.cfg b/tests/functional/registration/projects/flow_dsl/setup.cfg
deleted file mode 100644
index aec061de3..000000000
--- a/tests/functional/registration/projects/flow_dsl/setup.cfg
+++ /dev/null
@@ -1,15 +0,0 @@
-[metadata]
-name = flow_dsl
-version = 1.0.0
-
-[options]
-packages = find:
-install_requires =
-    textX
-
-[options.package_data]
-flow_dsl = *.tx
-
-[options.entry_points]
-textx_languages =
-    flow_dsl = flow_dsl:flow_dsl
diff --git a/tests/functional/registration/projects/flow_dsl/setup.py b/tests/functional/registration/projects/flow_dsl/setup.py
deleted file mode 100644
index 606849326..000000000
--- a/tests/functional/registration/projects/flow_dsl/setup.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from setuptools import setup
-
-setup()
diff --git a/tests/functional/registration/projects/flow_dsl/tests/test_flow_dsl.py b/tests/functional/registration/projects/flow_dsl/tests/test_flow_dsl.py
index d663dbf18..f7f16aa78 100644
--- a/tests/functional/registration/projects/flow_dsl/tests/test_flow_dsl.py
+++ b/tests/functional/registration/projects/flow_dsl/tests/test_flow_dsl.py
@@ -1,15 +1,18 @@
-from __future__ import unicode_literals
 import os
+
 import pytest
-from textx import (TextXSemanticError, TextXSyntaxError,
-                   metamodel_for_language,
-                   clear_language_registrations)
 
+from textx import (
+    TextXSemanticError,
+    TextXSyntaxError,
+    clear_language_registrations,
+    metamodel_for_language,
+)
 
 current_dir = os.path.dirname(__file__)
 
 
-@pytest.fixture(scope='module')
+@pytest.fixture(scope="module")
 def clear_all():
     clear_language_registrations()
 
@@ -19,10 +22,8 @@ def test_flow_dsl(clear_all):
     Test loading of correct flow model.
     """
 
-    mmF = metamodel_for_language('flow-dsl')
-    model = mmF.model_from_file(os.path.join(current_dir,
-                                             'models',
-                                             'data_flow.eflow'))
+    mmF = metamodel_for_language("flow-dsl")
+    model = mmF.model_from_file(os.path.join(current_dir, "models", "data_flow.eflow"))
     assert model is not None
     assert len(model.algos) == 2
     assert len(model.flows) == 1
@@ -33,12 +34,11 @@ def test_flow_dsl_validation(clear_all):
     Test flow model with semantic error raises error.
     """
 
-    mmF = metamodel_for_language('flow-dsl')
-    with pytest.raises(TextXSemanticError,
-                       match=r'.*algo data types must match.*'):
-        mmF.model_from_file(os.path.join(current_dir,
-                                         'models',
-                                         'data_flow_with_error.eflow'))
+    mmF = metamodel_for_language("flow-dsl")
+    with pytest.raises(TextXSemanticError, match=r".*algo data types must match.*"):
+        mmF.model_from_file(
+            os.path.join(current_dir, "models", "data_flow_with_error.eflow")
+        )
 
 
 def test_flow_dsl_types_validation(clear_all):
@@ -46,25 +46,15 @@ def test_flow_dsl_types_validation(clear_all):
     Test flow model including error raises error.
     """
 
-    # print("-----------------------------------1---")
-    # print(metamodel_for_language('flow-dsl')._tx_model_repository.all_models.filename_to_model.keys())
-    # print("----------------- #={}".format(
-    #    len(metamodel_for_language('flow-dsl')._tx_model_repository.all_models.filename_to_model.keys())))
-    mmF = metamodel_for_language('flow-dsl')
-    with pytest.raises(TextXSyntaxError,
-                       match=r'.*lowercase.*'):
-        mmF.model_from_file(os.path.join(current_dir,
-                                         'models',
-                                         'data_flow_including_error.eflow'))
+    mmF = metamodel_for_language("flow-dsl")
+    with pytest.raises(TextXSyntaxError, match=r".*lowercase.*"):
+        mmF.model_from_file(
+            os.path.join(current_dir, "models", "data_flow_including_error.eflow")
+        )
 
     # When reading a second time, the error must be reported again:
 
-    # print("-----------------------------------2---")
-    # print(metamodel_for_language('flow-dsl')._tx_model_repository.all_models.filename_to_model.keys())
-    # print("----------------- #={}".format(
-    #    len(metamodel_for_language('flow-dsl')._tx_model_repository.all_models.filename_to_model.keys())))
-    with pytest.raises(TextXSyntaxError,
-                       match=r'.*lowercase.*'):
-        mmF.model_from_file(os.path.join(current_dir,
-                                         'models',
-                                         'data_flow_including_error.eflow'))
+    with pytest.raises(TextXSyntaxError, match=r".*lowercase.*"):
+        mmF.model_from_file(
+            os.path.join(current_dir, "models", "data_flow_including_error.eflow")
+        )
diff --git a/tests/functional/registration/projects/flow_dsl/tests/test_issue196.py b/tests/functional/registration/projects/flow_dsl/tests/test_issue196.py
index beafe188b..c12b67b52 100644
--- a/tests/functional/registration/projects/flow_dsl/tests/test_issue196.py
+++ b/tests/functional/registration/projects/flow_dsl/tests/test_issue196.py
@@ -1,27 +1,28 @@
-from textx import metamodel_for_language
-from textx.exceptions import TextXError
 import os.path
+
 import pytest
 
+from textx import metamodel_for_language
+from textx.exceptions import TextXError
 
 current_dir = os.path.dirname(__file__)
 
 
 def test_issue196_errors_in_scope_provider_and_obj_processor():
-    mmF = metamodel_for_language('flow-dsl')
+    mmF = metamodel_for_language("flow-dsl")
 
     # print("-----------------------------------1---")
     # print(metamodel_for_language('flow-dsl').
     #      _tx_model_repository.all_models.filename_to_model.keys())
 
     cached_model_count = len(
-        metamodel_for_language('flow-dsl')._tx_model_repository.all_models)
+        metamodel_for_language("flow-dsl")._tx_model_repository.all_models
+    )
 
-    with pytest.raises(TextXError,
-                       match=r'.*types must be lowercase.*'):
-        mmF.model_from_file(os.path.join(current_dir,
-                                         'models',
-                                         'MODEL_WITH_IMPORT_ERROR.eflow'))
+    with pytest.raises(TextXError, match=r".*types must be lowercase.*"):
+        mmF.model_from_file(
+            os.path.join(current_dir, "models", "MODEL_WITH_IMPORT_ERROR.eflow")
+        )
 
     # print("-----------------------------------2---")
     # print(metamodel_for_language('flow-dsl').
@@ -29,14 +30,14 @@ def test_issue196_errors_in_scope_provider_and_obj_processor():
 
     # error while reading, no file cached (cached_model_count unchanged)!
     assert cached_model_count == len(
-        metamodel_for_language('flow-dsl')._tx_model_repository.all_models)
+        metamodel_for_language("flow-dsl")._tx_model_repository.all_models
+    )
 
-    with pytest.raises(TextXError,
-                       match=r'.*Unknown object "A" of class "Algo".*'):
+    with pytest.raises(TextXError, match=r'.*Unknown object "A" of class "Algo".*'):
         print("loading MODEL_WITH_TYPE_ERROR")
-        mmF.model_from_file(os.path.join(current_dir,
-                                         'models',
-                                         'MODEL_WITH_TYPE_ERROR.eflow'))
+        mmF.model_from_file(
+            os.path.join(current_dir, "models", "MODEL_WITH_TYPE_ERROR.eflow")
+        )
 
     # print("-----------------------------------3---")
     # print(metamodel_for_language('flow-dsl').
@@ -44,35 +45,35 @@ def test_issue196_errors_in_scope_provider_and_obj_processor():
 
     # error while reading, no file cached! (cached_model_count unchanged)!
     assert cached_model_count == len(
-        metamodel_for_language('flow-dsl')._tx_model_repository.all_models)
+        metamodel_for_language("flow-dsl")._tx_model_repository.all_models
+    )
 
-    with pytest.raises(TextXError,
-                       match=r'.*types must be lowercase.*'):
-        mmF.model_from_file(os.path.join(current_dir,
-                                         'models',
-                                         'MODEL_WITH_IMPORT_ERROR.eflow'))
+    with pytest.raises(TextXError, match=r".*types must be lowercase.*"):
+        mmF.model_from_file(
+            os.path.join(current_dir, "models", "MODEL_WITH_IMPORT_ERROR.eflow")
+        )
 
     # error while reading, no file cached! (cached_model_count unchanged)!
     assert cached_model_count == len(
-        metamodel_for_language('flow-dsl')._tx_model_repository.all_models)
+        metamodel_for_language("flow-dsl")._tx_model_repository.all_models
+    )
 
 
 def test_issue196_syntax_error_1():
-    mmF = metamodel_for_language('flow-dsl')
+    mmF = metamodel_for_language("flow-dsl")
 
     # print("-----------------------------------1---")
     # print(metamodel_for_language('flow-dsl').
     #      _tx_model_repository.all_models.filename_to_model.keys())
 
     cached_model_count = len(
-        metamodel_for_language('flow-dsl')._tx_model_repository.all_models)
+        metamodel_for_language("flow-dsl")._tx_model_repository.all_models
+    )
 
-    with pytest.raises(TextXError,
-                       match=r'.*\d+: Expected.*'):
+    with pytest.raises(TextXError, match=r".*\d+: Expected.*"):
         mmF.model_from_file(
-            os.path.join(current_dir,
-                         'models',
-                         'MODEL_WITH_IMPORT_SYNTAX_ERROR.eflow'))
+            os.path.join(current_dir, "models", "MODEL_WITH_IMPORT_SYNTAX_ERROR.eflow")
+        )
 
     # print("-----------------------------------2---")
     # print(metamodel_for_language('flow-dsl').
@@ -80,24 +81,25 @@ def test_issue196_syntax_error_1():
 
     # error while reading, no file cached!
     assert cached_model_count == len(
-        metamodel_for_language('flow-dsl')._tx_model_repository.all_models)
+        metamodel_for_language("flow-dsl")._tx_model_repository.all_models
+    )
 
 
 def test_issue196_syntax_error_2():
-    mmF = metamodel_for_language('flow-dsl')
+    mmF = metamodel_for_language("flow-dsl")
 
     # print("-----------------------------------1---")
     # print(metamodel_for_language('flow-dsl').
     #      _tx_model_repository.all_models.filename_to_model.keys())
 
     cached_model_count = len(
-        metamodel_for_language('flow-dsl')._tx_model_repository.all_models)
+        metamodel_for_language("flow-dsl")._tx_model_repository.all_models
+    )
 
-    with pytest.raises(TextXError,
-                       match=r'.*\d+: Expected.*'):
-        mmF.model_from_file(os.path.join(current_dir,
-                                         'models',
-                                         'MODEL_WITH_SYNTAX_ERROR.eflow'))
+    with pytest.raises(TextXError, match=r".*\d+: Expected.*"):
+        mmF.model_from_file(
+            os.path.join(current_dir, "models", "MODEL_WITH_SYNTAX_ERROR.eflow")
+        )
 
     # print("-----------------------------------2---")
     # print(metamodel_for_language('flow-dsl').
@@ -105,4 +107,5 @@ def test_issue196_syntax_error_2():
 
     # error while reading, no file cached! (cached_model_count unchanged)!
     assert cached_model_count == len(
-        metamodel_for_language('flow-dsl')._tx_model_repository.all_models)
+        metamodel_for_language("flow-dsl")._tx_model_repository.all_models
+    )
diff --git a/tests/functional/registration/projects/types_dsl/pyproject.toml b/tests/functional/registration/projects/types_dsl/pyproject.toml
new file mode 100644
index 000000000..345d96436
--- /dev/null
+++ b/tests/functional/registration/projects/types_dsl/pyproject.toml
@@ -0,0 +1,10 @@
+[project]
+name = "types_dsl"
+version = "1.0.0"
+
+dependencies = [
+    "textX"
+]
+
+[project.entry-points.textx_languages]
+types_dsl = "types_dsl:types_dsl"
diff --git a/tests/functional/registration/projects/types_dsl/setup.cfg b/tests/functional/registration/projects/types_dsl/setup.cfg
deleted file mode 100644
index 0c6d9903a..000000000
--- a/tests/functional/registration/projects/types_dsl/setup.cfg
+++ /dev/null
@@ -1,15 +0,0 @@
-[metadata]
-name = types_dsl
-version = 1.0.0
-
-[options]
-packages = find:
-install_requires =
-    textX
-
-[options.package_data]
-types_dsl = *.tx
-
-[options.entry_points]
-textx_languages =
-    types_dsl = types_dsl:types_dsl 
\ No newline at end of file
diff --git a/tests/functional/registration/projects/types_dsl/setup.py b/tests/functional/registration/projects/types_dsl/setup.py
deleted file mode 100644
index 606849326..000000000
--- a/tests/functional/registration/projects/types_dsl/setup.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from setuptools import setup
-
-setup()
diff --git a/tests/functional/registration/projects/types_dsl/tests/test_types_dsl.py b/tests/functional/registration/projects/types_dsl/tests/test_types_dsl.py
index 74f4279e8..450ce3a21 100644
--- a/tests/functional/registration/projects/types_dsl/tests/test_types_dsl.py
+++ b/tests/functional/registration/projects/types_dsl/tests/test_types_dsl.py
@@ -1,14 +1,13 @@
-from __future__ import unicode_literals
-import pytest
 import os
-from textx import (TextXSyntaxError, metamodel_for_language,
-                   clear_language_registrations)
 
+import pytest
+
+from textx import TextXSyntaxError, clear_language_registrations, metamodel_for_language
 
 current_dir = os.path.dirname(__file__)
 
 
-@pytest.fixture(scope='module')
+@pytest.fixture(scope="module")
 def clear_all():
     clear_language_registrations()
 
@@ -18,10 +17,8 @@ def test_types_dsl(clear_all):
     Test loading of correct types model.
     """
 
-    mmT = metamodel_for_language('types-dsl')
-    model = mmT.model_from_file(os.path.join(current_dir,
-                                             'models',
-                                             'types.etype'))
+    mmT = metamodel_for_language("types-dsl")
+    model = mmT.model_from_file(os.path.join(current_dir, "models", "types.etype"))
     assert model is not None
     assert len(model.types) == 2
 
@@ -31,9 +28,6 @@ def test_types_dsl_invalid(clear_all):
     Test that types model with semantic error raises the error.
     """
 
-    mmT = metamodel_for_language('types-dsl')
-    with pytest.raises(TextXSyntaxError,
-                       match=r'.*lowercase.*'):
-        mmT.model_from_file(os.path.join(current_dir,
-                                         'models',
-                                         'types_with_error.etype'))
+    mmT = metamodel_for_language("types-dsl")
+    with pytest.raises(TextXSyntaxError, match=r".*lowercase.*"):
+        mmT.model_from_file(os.path.join(current_dir, "models", "types_with_error.etype"))
diff --git a/tests/functional/registration/projects/types_dsl/types_dsl/__init__.py b/tests/functional/registration/projects/types_dsl/types_dsl/__init__.py
index 9f6697e27..a5bed6bd0 100644
--- a/tests/functional/registration/projects/types_dsl/types_dsl/__init__.py
+++ b/tests/functional/registration/projects/types_dsl/types_dsl/__init__.py
@@ -1,34 +1,28 @@
 import os
-from textx import metamodel_from_file, TextXSyntaxError, language, get_model, get_location
 
+from textx import TextXSyntaxError, get_location, get_model, language, metamodel_from_file
 
-@language('types-dsl', '*.etype')
+
+@language("types-dsl", "*.etype")
 def types_dsl():
     """
     An example DSL for simple types definition
     """
     current_dir = os.path.dirname(__file__)
-    p = os.path.join(current_dir, 'Types.tx')
+    p = os.path.join(current_dir, "Types.tx")
     types_mm = metamodel_from_file(p, global_repository=True)
     types_mm.model_param_defs.add(
-        'type_name_check',
-        'enables checks on the type name (default="on" or "off")'
+        "type_name_check", 'enables checks on the type name (default="on" or "off")'
     )
 
     def check_type(t):
         type_name_check = get_model(t)._tx_model_params.get(
-            'type_name_check', default='on'
+            "type_name_check", default="on"
         )
-        assert type_name_check in ['on', 'off']
-        if type_name_check == 'on':
-            if t.name[0].isupper():
-                raise TextXSyntaxError(
-                    "types must be lowercase",
-                    **get_location(t)
-                )
+        assert type_name_check in ["on", "off"]
+        if type_name_check == "on" and t.name[0].isupper():
+            raise TextXSyntaxError("types must be lowercase", **get_location(t))
 
-    types_mm.register_obj_processors({
-        'Type': check_type
-    })
+    types_mm.register_obj_processors({"Type": check_type})
 
     return types_mm
diff --git a/tests/functional/registration/test_check.py b/tests/functional/registration/test_check.py
index d9dcd93fe..b74261fe1 100644
--- a/tests/functional/registration/test_check.py
+++ b/tests/functional/registration/test_check.py
@@ -2,40 +2,41 @@
 Test check/validation command.
 """
 import os
-from textx.cli import textx
+
 from click.testing import CliRunner
 from pytest import raises
-from textx.registration import metamodel_for_language
-from textx.exceptions import TextXSyntaxError
 
+from textx.cli import textx
+from textx.exceptions import TextXSyntaxError
+from textx.registration import metamodel_for_language
 
 this_folder = os.path.abspath(os.path.dirname(__file__))
 
 
 def test_object_processor_with_optional_parameter_default():
-    mm = metamodel_for_language('types-dsl')
-    model_file = os.path.join(this_folder,
-                              'projects', 'types_dsl', 'tests',
-                              'models', 'types_with_error.etype')
-    with raises(TextXSyntaxError, match='types must be lowercase'):
+    mm = metamodel_for_language("types-dsl")
+    model_file = os.path.join(
+        this_folder, "projects", "types_dsl", "tests", "models", "types_with_error.etype"
+    )
+    with raises(TextXSyntaxError, match="types must be lowercase"):
         mm.model_from_file(model_file)
 
 
 def test_object_processor_with_optional_parameter_on():
-    mm = metamodel_for_language('types-dsl')
-    model_file = os.path.join(this_folder,
-                              'projects', 'types_dsl', 'tests',
-                              'models', 'types_with_error.etype')
-    with raises(TextXSyntaxError, match='types must be lowercase'):
-        mm.model_from_file(model_file, type_name_check='on')
+    mm = metamodel_for_language("types-dsl")
+    model_file = os.path.join(
+        this_folder, "projects", "types_dsl", "tests", "models", "types_with_error.etype"
+    )
+    with raises(TextXSyntaxError, match="types must be lowercase"):
+        mm.model_from_file(model_file, type_name_check="on")
 
 
 def test_object_processor_with_optional_parameter_off():
-    mm = metamodel_for_language('types-dsl')
-    model_file = os.path.join(this_folder,
-                              'projects', 'types_dsl', 'tests',
-                              'models', 'types_with_error.etype')
-    mm.model_from_file(model_file, type_name_check='off')
+    mm = metamodel_for_language("types-dsl")
+    model_file = os.path.join(
+        this_folder, "projects", "types_dsl", "tests", "models", "types_with_error.etype"
+    )
+    mm.model_from_file(model_file, type_name_check="off")
 
 
 def test_check_metamodel():
@@ -43,67 +44,68 @@ def test_check_metamodel():
     Meta-model is also a model.
     """
 
-    metamodel_file = os.path.join(this_folder,
-                                  'projects', 'flow_dsl', 'flow_dsl',
-                                  'Flow.tx')
+    metamodel_file = os.path.join(
+        this_folder, "projects", "flow_dsl", "flow_dsl", "Flow.tx"
+    )
     runner = CliRunner()
-    result = runner.invoke(textx, ['check', metamodel_file])
+    result = runner.invoke(textx, ["check", metamodel_file])
     assert result.exit_code == 0
-    assert 'Flow.tx: OK.' in result.output
+    assert "Flow.tx: OK." in result.output
 
 
 def test_check_valid_model():
-    model_file = os.path.join(this_folder,
-                              'projects', 'flow_dsl', 'tests',
-                              'models', 'data_flow.eflow')
+    model_file = os.path.join(
+        this_folder, "projects", "flow_dsl", "tests", "models", "data_flow.eflow"
+    )
     runner = CliRunner()
-    result = runner.invoke(textx, ['check', model_file])
+    result = runner.invoke(textx, ["check", model_file])
     assert result.exit_code == 0
-    assert 'data_flow.eflow: OK.' in result.output
+    assert "data_flow.eflow: OK." in result.output
 
 
 def test_check_valid_model_with_explicit_language_name():
     """
     Test checking of model where language name is given explicitly.
     """
-    model_file = os.path.join(this_folder,
-                              'projects', 'flow_dsl', 'tests',
-                              'models', 'data_flow.eflow')
+    model_file = os.path.join(
+        this_folder, "projects", "flow_dsl", "tests", "models", "data_flow.eflow"
+    )
     runner = CliRunner()
-    result = runner.invoke(textx, ['check',
-                                   '--language', 'flow-dsl',
-                                   model_file])
+    result = runner.invoke(textx, ["check", "--language", "flow-dsl", model_file])
     assert result.exit_code == 0
-    assert 'data_flow.eflow: OK.' in result.output
+    assert "data_flow.eflow: OK." in result.output
 
 
 def test_check_valid_model_with_metamodel_from_file():
     """
     Test checking of model where meta-model is provided from the grammar file.
     """
-    model_file = os.path.join(this_folder,
-                              'projects', 'types_dsl', 'tests',
-                              'models', 'types.etype')
-    metamodel_file = os.path.join(this_folder,
-                                  'projects', 'types_dsl', 'types_dsl',
-                                  'Types.tx')
+    model_file = os.path.join(
+        this_folder, "projects", "types_dsl", "tests", "models", "types.etype"
+    )
+    metamodel_file = os.path.join(
+        this_folder, "projects", "types_dsl", "types_dsl", "Types.tx"
+    )
     runner = CliRunner()
-    result = runner.invoke(textx, ['check',
-                                   '--grammar', metamodel_file,
-                                   model_file])
+    result = runner.invoke(textx, ["check", "--grammar", metamodel_file, model_file])
     assert result.exit_code == 0
-    assert 'types.etype: OK.' in result.output
+    assert "types.etype: OK." in result.output
 
 
 def test_check_invalid_model():
-    model_file = os.path.join(this_folder,
-                              'projects', 'flow_dsl', 'tests',
-                              'models', 'data_flow_including_error.eflow')
+    model_file = os.path.join(
+        this_folder,
+        "projects",
+        "flow_dsl",
+        "tests",
+        "models",
+        "data_flow_including_error.eflow",
+    )
     runner = CliRunner()
-    result = runner.invoke(textx, ['check', model_file])
+    result = runner.invoke(textx, ["check", model_file])
     assert result.exit_code != 0
-    assert 'Error:' in result.output
-    assert 'types must be lowercase' in result.output
+    assert "Error:" in result.output
+    assert "types must be lowercase" in result.output
 
 
 def test_check_invalid_language():
@@ -112,6 +114,6 @@ def test_check_invalid_language():
     """
 
     runner = CliRunner()
-    result = runner.invoke(textx, ['check', 'some_unexisting_file'])
+    result = runner.invoke(textx, ["check", "some_unexisting_file"])
 
-    assert 'No language registered that can parse' in result.output
+    assert "No language registered that can parse" in result.output
diff --git a/tests/functional/registration/test_discovery.py b/tests/functional/registration/test_discovery.py
index 928886c4f..3d644f79b 100644
--- a/tests/functional/registration/test_discovery.py
+++ b/tests/functional/registration/test_discovery.py
@@ -1,20 +1,21 @@
 """
 Test discovering of registered languages and generators.
 """
-from textx.cli import textx
 from click.testing import CliRunner
 
+from textx.cli import textx
+
 
 def test_list_languages_cli():
     """
     Test list-languages command.
     """
     runner = CliRunner()
-    result = runner.invoke(textx, ['list-languages'])
+    result = runner.invoke(textx, ["list-languages"])
     assert result.exit_code == 0
-    assert 'flow-dsl[1.0.0]' in result.output
-    assert '*.eflow' in result.output
-    assert 'data-dsl' in result.output
+    assert "flow-dsl[1.0.0]" in result.output
+    assert "*.eflow" in result.output
+    assert "data-dsl" in result.output
 
 
 def test_list_generators_cli():
@@ -22,7 +23,7 @@ def test_list_generators_cli():
     Test list-generators command.
     """
     runner = CliRunner()
-    result = runner.invoke(textx, ['list-generators'])
+    result = runner.invoke(textx, ["list-generators"])
     assert result.exit_code == 0
-    assert 'flow-dsl -> PlantUML' in result.output
-    assert 'flow-codegen[1.0.0]' in result.output
+    assert "flow-dsl -> PlantUML" in result.output
+    assert "flow-codegen[1.0.0]" in result.output
diff --git a/tests/functional/registration/test_generate.py b/tests/functional/registration/test_generate.py
index e58e83c60..10366ba5b 100644
--- a/tests/functional/registration/test_generate.py
+++ b/tests/functional/registration/test_generate.py
@@ -2,26 +2,28 @@
 Tests for `generate` command.
 """
 import os
+from contextlib import suppress
+
 import pytest
-from textx.cli import textx
 from click.testing import CliRunner
 
+from textx.cli import textx
+
 this_folder = os.path.abspath(os.path.dirname(__file__))
 
 
 @pytest.fixture
 def model_file():
+    with suppress(OSError):
+        os.remove(
+            os.path.join(
+                this_folder, "projects", "flow_dsl", "tests", "models", "data_flow.pu"
+            )
+        )
 
-    try:
-        os.remove(os.path.join(this_folder,
-                               'projects', 'flow_dsl', 'tests',
-                               'models', 'data_flow.pu'))
-    except OSError:
-        pass
-
-    return os.path.join(this_folder,
-                        'projects', 'flow_dsl', 'tests',
-                        'models', 'data_flow.eflow')
+    return os.path.join(
+        this_folder, "projects", "flow_dsl", "tests", "models", "data_flow.eflow"
+    )
 
 
 def test_generator_registered():
@@ -29,9 +31,9 @@ def test_generator_registered():
     That that generator from flow to PlantUML is registered
     """
     runner = CliRunner()
-    result = runner.invoke(textx, ['list-generators'])
+    result = runner.invoke(textx, ["list-generators"])
     assert result.exit_code == 0
-    assert 'flow-dsl -> PlantUML' in result.output
+    assert "flow-dsl -> PlantUML" in result.output
 
 
 def test_generating_flow_model(model_file):
@@ -39,16 +41,18 @@ def test_generating_flow_model(model_file):
     Test that generator can be called.
     """
     runner = CliRunner()
-    result = runner.invoke(textx, ['generate',
-                                   '--target', 'PlantUML',
-                                   '--overwrite', model_file])
+    result = runner.invoke(
+        textx, ["generate", "--target", "PlantUML", "--overwrite", model_file]
+    )
     assert result.exit_code == 0
-    assert 'Generating PlantUML target from models' in result.output
-    assert '->' in result.output
-    assert 'models/data_flow.pu' in result.output
-    assert os.path.exists(os.path.join(this_folder,
-                                       'projects', 'flow_dsl', 'tests',
-                                       'models', 'data_flow.pu'))
+    assert "Generating PlantUML target from models" in result.output
+    assert "->" in result.output
+    assert "models/data_flow.pu" in result.output
+    assert os.path.exists(
+        os.path.join(
+            this_folder, "projects", "flow_dsl", "tests", "models", "data_flow.pu"
+        )
+    )
 
 
 def test_generate_by_providing_explicit_language_name(model_file):
@@ -56,17 +60,27 @@ def test_generate_by_providing_explicit_language_name(model_file):
     Test running generator by providing an explicit language name.
     """
     runner = CliRunner()
-    result = runner.invoke(textx, ['generate',
-                                   '--language', 'flow-dsl',
-                                   '--target', 'PlantUML',
-                                   '--overwrite', model_file])
+    result = runner.invoke(
+        textx,
+        [
+            "generate",
+            "--language",
+            "flow-dsl",
+            "--target",
+            "PlantUML",
+            "--overwrite",
+            model_file,
+        ],
+    )
     assert result.exit_code == 0
-    assert 'Generating PlantUML target from models' in result.output
-    assert '->' in result.output
-    assert 'models/data_flow.pu' in result.output
-    assert os.path.exists(os.path.join(this_folder,
-                                       'projects', 'flow_dsl', 'tests',
-                                       'models', 'data_flow.pu'))
+    assert "Generating PlantUML target from models" in result.output
+    assert "->" in result.output
+    assert "models/data_flow.pu" in result.output
+    assert os.path.exists(
+        os.path.join(
+            this_folder, "projects", "flow_dsl", "tests", "models", "data_flow.pu"
+        )
+    )
 
 
 def test_passing_custom_arguments_to_generator(model_file):
@@ -74,25 +88,34 @@ def test_passing_custom_arguments_to_generator(model_file):
     Test passing custom arguments from command line to the generator.
     """
     runner = CliRunner()
-    result = runner.invoke(textx, ['generate',
-                                   '--language', 'flow-dsl',
-                                   '--target', 'PlantUML',
-                                   model_file,
-                                   '--custom1', '42',
-                                   '--custom2', '"some string"',
-                                   '--overwrite'])
+    result = runner.invoke(
+        textx,
+        [
+            "generate",
+            "--language",
+            "flow-dsl",
+            "--target",
+            "PlantUML",
+            model_file,
+            "--custom1",
+            "42",
+            "--custom2",
+            '"some string"',
+            "--overwrite",
+        ],
+    )
     assert result.exit_code == 0
-    assert 'Generating PlantUML target from models' in result.output
-    assert '->' in result.output
-    assert 'models/data_flow.pu' in result.output
-    target_file = os.path.join(this_folder,
-                               'projects', 'flow_dsl', 'tests',
-                               'models', 'data_flow.pu')
+    assert "Generating PlantUML target from models" in result.output
+    assert "->" in result.output
+    assert "models/data_flow.pu" in result.output
+    target_file = os.path.join(
+        this_folder, "projects", "flow_dsl", "tests", "models", "data_flow.pu"
+    )
     assert os.path.exists(target_file)
-    with open(target_file, 'r') as f:
+    with open(target_file) as f:
         content = f.read()
-    assert 'custom1=42' in content
-    assert 'custom2=some string' in content
+    assert "custom1=42" in content
+    assert "custom2=some string" in content
 
 
 def test_generate_for_invalid_file_raises_error():
@@ -100,8 +123,9 @@ def test_generate_for_invalid_file_raises_error():
     Test running generator by providing an explicit language name.
     """
     runner = CliRunner()
-    result = runner.invoke(textx, ['generate',
-                                   '--target', 'PlantUML',
-                                   '--overwrite', 'unexistingmodel.invalid'])
+    result = runner.invoke(
+        textx,
+        ["generate", "--target", "PlantUML", "--overwrite", "unexistingmodel.invalid"],
+    )
 
-    assert 'No language registered that can parse' in result.output
+    assert "No language registered that can parse" in result.output
diff --git a/tests/functional/registration/test_registration.py b/tests/functional/registration/test_registration.py
index c5981a6e1..67946b145 100644
--- a/tests/functional/registration/test_registration.py
+++ b/tests/functional/registration/test_registration.py
@@ -1,17 +1,28 @@
-from __future__ import unicode_literals
 import pytest
-from textx import (metamodel_from_str,
-                   clear_language_registrations, clear_generator_registrations,
-                   register_language, language, register_generator, generator,
-                   language_description, generator_description,
-                   language_for_file, languages_for_file,
-                   metamodel_for_language, metamodel_for_file,
-                   LanguageDesc, GeneratorDesc, TextXRegistrationError)
+
+from textx import (
+    GeneratorDesc,
+    LanguageDesc,
+    TextXRegistrationError,
+    clear_generator_registrations,
+    clear_language_registrations,
+    generator,
+    generator_description,
+    language,
+    language_description,
+    language_for_file,
+    languages_for_file,
+    metamodel_for_file,
+    metamodel_for_language,
+    metamodel_from_str,
+    register_generator,
+    register_language,
+)
 from textx.metamodel import TextXMetaModel
 
 
 def mymetamodel_callable(**kwargs):
-    return metamodel_from_str('MyModel: INT;', **kwargs)
+    return metamodel_from_str("MyModel: INT;", **kwargs)
 
 
 def generator_callable():
@@ -21,10 +32,12 @@ def generator_callable():
 @pytest.fixture
 def language_registered():
     clear_language_registrations()
-    register_language('test-lang',
-                      pattern='*.test',
-                      description='test-lang description',
-                      metamodel=mymetamodel_callable)
+    register_language(
+        "test-lang",
+        pattern="*.test",
+        description="test-lang description",
+        metamodel=mymetamodel_callable,
+    )
 
 
 def test_register_language():
@@ -32,31 +45,37 @@ def test_register_language():
     Test both style of language registration.
     """
     clear_language_registrations()
-    register_language('test-lang',
-                      pattern='*.test',
-                      description='test-lang description',
-                      metamodel=mymetamodel_callable)
+    register_language(
+        "test-lang",
+        pattern="*.test",
+        description="test-lang description",
+        metamodel=mymetamodel_callable,
+    )
 
-    language = language_description('test-lang')
+    language = language_description("test-lang")
 
     assert type(language) is LanguageDesc
-    assert language.name == 'test-lang'
-    assert language.pattern == '*.test'
-    assert language.description == 'test-lang description'
+    assert language.name == "test-lang"
+    assert language.pattern == "*.test"
+    assert language.description == "test-lang description"
     assert language.metamodel == mymetamodel_callable
 
     clear_language_registrations()
-    register_language(LanguageDesc('test-lang',
-                                   pattern='*.test',
-                                   description='test-lang description',
-                                   metamodel=mymetamodel_callable))
+    register_language(
+        LanguageDesc(
+            "test-lang",
+            pattern="*.test",
+            description="test-lang description",
+            metamodel=mymetamodel_callable,
+        )
+    )
 
-    language = language_description('test-lang')
+    language = language_description("test-lang")
 
     assert type(language) is LanguageDesc
-    assert language.name == 'test-lang'
-    assert language.pattern == '*.test'
-    assert language.description == 'test-lang description'
+    assert language.name == "test-lang"
+    assert language.pattern == "*.test"
+    assert language.description == "test-lang description"
     assert language.metamodel == mymetamodel_callable
 
 
@@ -67,23 +86,22 @@ def test_register_language_with_decorator():
 
     clear_language_registrations()
 
-    @language('test-lang', '*.test')
+    @language("test-lang", "*.test")
     def test_lang():
         "This is a test language"
         return mymetamodel_callable()
 
     assert type(test_lang) is LanguageDesc
-    assert test_lang.name == 'test-lang'
-    assert test_lang.pattern == '*.test'
-    assert test_lang.description == 'This is a test language'
+    assert test_lang.name == "test-lang"
+    assert test_lang.pattern == "*.test"
+    assert test_lang.description == "This is a test language"
     assert callable(test_lang.metamodel)
 
 
 def test_asking_for_unregistered_language_raises_error():
-    """
-    """
-    with pytest.raises(TextXRegistrationError, match='.*not registered.*'):
-        language_description('unexisting')
+    """ """
+    with pytest.raises(TextXRegistrationError, match=".*not registered.*"):
+        language_description("unexisting")
 
 
 def test_register_already_existing_language():
@@ -93,16 +111,20 @@ def test_register_already_existing_language():
     """
 
     clear_language_registrations()
-    register_language('test-lang',
-                      pattern='*.test',
-                      description='test-lang description',
-                      metamodel=mymetamodel_callable)
-
-    with pytest.raises(TextXRegistrationError, match='.*already registered.*'):
-        register_language('test-lang',
-                          pattern='*.test',
-                          description='test-lang description',
-                          metamodel=mymetamodel_callable)
+    register_language(
+        "test-lang",
+        pattern="*.test",
+        description="test-lang description",
+        metamodel=mymetamodel_callable,
+    )
+
+    with pytest.raises(TextXRegistrationError, match=".*already registered.*"):
+        register_language(
+            "test-lang",
+            pattern="*.test",
+            description="test-lang description",
+            metamodel=mymetamodel_callable,
+        )
 
 
 def test_declaratively_registered_languages_always_available():
@@ -112,11 +134,11 @@ def test_declaratively_registered_languages_always_available():
     accessible.
     """
     clear_language_registrations()
-    tx_lang = language_description('textx')
+    tx_lang = language_description("textx")
 
-    assert tx_lang.name == 'textX'
-    assert tx_lang.pattern == '*.tx'
-    assert tx_lang.project_name == 'textX'
+    assert tx_lang.name == "textX"
+    assert tx_lang.pattern == "*.tx"
+    assert tx_lang.project_name == "textX"
 
 
 def test_language_for_file():
@@ -125,10 +147,10 @@ def test_language_for_file():
     """
 
     clear_language_registrations()
-    tx_lang = language_description('textx')
+    tx_lang = language_description("textx")
 
-    assert tx_lang is language_for_file('test.tx')
-    assert tx_lang is language_for_file('*.tx')
+    assert tx_lang is language_for_file("test.tx")
+    assert tx_lang is language_for_file("*.tx")
 
 
 def test_metamodel_for_language(language_registered):
@@ -136,7 +158,7 @@ def test_metamodel_for_language(language_registered):
     Test finding a meta-model for the given language name.
     """
 
-    mm = metamodel_for_language('test-lang')
+    mm = metamodel_for_language("test-lang")
     assert isinstance(mm, TextXMetaModel)
 
 
@@ -146,36 +168,35 @@ def test_metamodel_for_language_with_params(language_registered):
     brand new meta-model while the old instance will be returned if no `kwargs`
     are given.
     """
+
     class MyModel:
         pass
 
-    mm = metamodel_for_language('test-lang', ignore_case=True,
-                                classes=[MyModel])
+    mm = metamodel_for_language("test-lang", ignore_case=True, classes=[MyModel])
     assert mm.ignore_case
-    assert 'MyModel' in mm.user_classes
+    assert "MyModel" in mm.user_classes
 
     # Now we can call without kwargs and we still get the same instance
-    mm2 = metamodel_for_language('test-lang')
+    mm2 = metamodel_for_language("test-lang")
     assert mm is mm2
 
     # But, passing kwargs again creates a new meta-model
-    mm3 = metamodel_for_language('test-lang', ignore_case=False,
-                                 classes=[MyModel])
+    mm3 = metamodel_for_language("test-lang", ignore_case=False, classes=[MyModel])
     assert not mm3.ignore_case
-    assert 'MyModel' in mm.user_classes
+    assert "MyModel" in mm.user_classes
 
 
 def test_metamodel_for_file(language_registered):
     """
     Test finding a meta-model for the given file or file pattern.
     """
-    mm = metamodel_for_file('mymodel.test')
+    mm = metamodel_for_file("mymodel.test")
     assert isinstance(mm, TextXMetaModel)
 
-    mm = metamodel_for_file('*.test')
+    mm = metamodel_for_file("*.test")
     assert isinstance(mm, TextXMetaModel)
 
-    mm = metamodel_for_file('somefile*.test')
+    mm = metamodel_for_file("somefile*.test")
     assert isinstance(mm, TextXMetaModel)
 
 
@@ -185,21 +206,22 @@ def test_metamodel_for_file_with_params(language_registered):
     brand new meta-model while the old instance will be returned if no `kwargs`
     are given.
     """
+
     class MyModel:
         pass
 
-    mm = metamodel_for_file('*.test', ignore_case=True, classes=[MyModel])
+    mm = metamodel_for_file("*.test", ignore_case=True, classes=[MyModel])
     assert mm.ignore_case
-    assert 'MyModel' in mm.user_classes
+    assert "MyModel" in mm.user_classes
 
     # Now we can call without kwargs and we still get the same instance
-    mm2 = metamodel_for_file('*.test')
+    mm2 = metamodel_for_file("*.test")
     assert mm is mm2
 
     # But, passing kwargs again creates a new meta-model
-    mm3 = metamodel_for_file('*.test', ignore_case=False, classes=[MyModel])
+    mm3 = metamodel_for_file("*.test", ignore_case=False, classes=[MyModel])
     assert not mm3.ignore_case
-    assert 'MyModel' in mm.user_classes
+    assert "MyModel" in mm.user_classes
 
 
 def test_multiple_languages_for_the_same_pattern():
@@ -208,20 +230,23 @@ def test_multiple_languages_for_the_same_pattern():
     `language_for_file` shall raise `TextXRegistrationError`.
     """
     clear_language_registrations()
-    register_language('test-lang',
-                      pattern='*.test',
-                      description='test-lang description',
-                      metamodel=mymetamodel_callable)
-    register_language('test-lang2',
-                      pattern='*.test',
-                      description='test-lang2 description',
-                      metamodel=mymetamodel_callable)
+    register_language(
+        "test-lang",
+        pattern="*.test",
+        description="test-lang description",
+        metamodel=mymetamodel_callable,
+    )
+    register_language(
+        "test-lang2",
+        pattern="*.test",
+        description="test-lang2 description",
+        metamodel=mymetamodel_callable,
+    )
 
-    with pytest.raises(TextXRegistrationError,
-                       match='Multiple languages can parse.*'):
-        language_for_file('Somefile.test')
+    with pytest.raises(TextXRegistrationError, match="Multiple languages can parse.*"):
+        language_for_file("Somefile.test")
 
-    assert len(languages_for_file('Somefile.test')) == 2
+    assert len(languages_for_file("Somefile.test")) == 2
 
 
 def test_file_without_any_language():
@@ -230,9 +255,10 @@ def test_file_without_any_language():
     no languages are registered will raise an error.
     """
 
-    with pytest.raises(TextXRegistrationError,
-                       match='No language registered that can parse.*'):
-        language_for_file('Somefile.nolang')
+    with pytest.raises(
+        TextXRegistrationError, match="No language registered that can parse.*"
+    ):
+        language_for_file("Somefile.nolang")
 
 
 def test_metamodel_callable_must_return_a_metamodel():
@@ -244,14 +270,15 @@ def invalid_metamodel_callable():
         return 42
 
     clear_language_registrations()
-    register_language('test-lang',
-                      pattern='*.test',
-                      description='test-lang description',
-                      metamodel=invalid_metamodel_callable)
+    register_language(
+        "test-lang",
+        pattern="*.test",
+        description="test-lang description",
+        metamodel=invalid_metamodel_callable,
+    )
 
-    with pytest.raises(TextXRegistrationError,
-                       match='.*Meta-model type for language.*'):
-        metamodel_for_language('test-lang')
+    with pytest.raises(TextXRegistrationError, match=".*Meta-model type for language.*"):
+        metamodel_for_language("test-lang")
 
 
 def test_register_generator():
@@ -260,31 +287,37 @@ def test_register_generator():
     """
 
     clear_generator_registrations()
-    register_generator('test-lang',
-                       target='Java',
-                       description='test-lang Java generator',
-                       generator=generator_callable)
+    register_generator(
+        "test-lang",
+        target="Java",
+        description="test-lang Java generator",
+        generator=generator_callable,
+    )
 
-    generator = generator_description('test-lang', 'java')
+    generator = generator_description("test-lang", "java")
 
     assert type(generator) is GeneratorDesc
-    assert generator.language == 'test-lang'
-    assert generator.target == 'Java'
-    assert generator.description == 'test-lang Java generator'
+    assert generator.language == "test-lang"
+    assert generator.target == "Java"
+    assert generator.description == "test-lang Java generator"
     assert generator.generator == generator_callable
 
     clear_generator_registrations()
-    register_generator(GeneratorDesc('test-lang',
-                                     target='Java',
-                                     description='test-lang Java generator',
-                                     generator=generator_callable))
+    register_generator(
+        GeneratorDesc(
+            "test-lang",
+            target="Java",
+            description="test-lang Java generator",
+            generator=generator_callable,
+        )
+    )
 
-    generator = generator_description('test-lang', 'java')
+    generator = generator_description("test-lang", "java")
 
     assert type(generator) is GeneratorDesc
-    assert generator.language == 'test-lang'
-    assert generator.target == 'Java'
-    assert generator.description == 'test-lang Java generator'
+    assert generator.language == "test-lang"
+    assert generator.target == "Java"
+    assert generator.description == "test-lang Java generator"
     assert generator.generator == generator_callable
 
 
@@ -292,15 +325,16 @@ def test_register_generator_with_decorator():
     """
     Test using `generator` decorator to register a generator function.
     """
-    @generator('test-lang', 'Java')
+
+    @generator("test-lang", "Java")
     def generator_for_test_lang():
         "Generator description"
         pass
 
     assert type(generator_for_test_lang) is GeneratorDesc
-    assert generator_for_test_lang.language == 'test-lang'
-    assert generator_for_test_lang.target == 'Java'
-    assert generator_for_test_lang.description == 'Generator description'
+    assert generator_for_test_lang.language == "test-lang"
+    assert generator_for_test_lang.target == "Java"
+    assert generator_for_test_lang.description == "Generator description"
     assert callable(generator_for_test_lang.generator)
 
 
@@ -308,11 +342,11 @@ def test_asking_for_unregistered_generator_raises_error():
     """
     Test searching for unexisting generator raises an error.
     """
-    with pytest.raises(TextXRegistrationError, match='.*No generators.*'):
-        generator_description('flow-dsl', 'unexisting_target')
+    with pytest.raises(TextXRegistrationError, match=".*No generators.*"):
+        generator_description("flow-dsl", "unexisting_target")
 
-    with pytest.raises(TextXRegistrationError, match='.*No generators.*'):
-        generator_description('unexisting_language', 'unexisting_target')
+    with pytest.raises(TextXRegistrationError, match=".*No generators.*"):
+        generator_description("unexisting_language", "unexisting_target")
 
 
 def test_register_already_existing_generator():
@@ -322,16 +356,20 @@ def test_register_already_existing_generator():
     """
 
     clear_generator_registrations()
-    register_generator('test-lang',
-                       target='Java',
-                       description='test-lang Java generator',
-                       generator=generator_callable)
-
-    with pytest.raises(TextXRegistrationError, match='.*already registered.*'):
-        register_generator('test-lang',
-                           target='Java',
-                           description='test-lang Java generator',
-                           generator=generator_callable)
+    register_generator(
+        "test-lang",
+        target="Java",
+        description="test-lang Java generator",
+        generator=generator_callable,
+    )
+
+    with pytest.raises(TextXRegistrationError, match=".*already registered.*"):
+        register_generator(
+            "test-lang",
+            target="Java",
+            description="test-lang Java generator",
+            generator=generator_callable,
+        )
 
 
 def test_declaratively_registered_generator_always_available():
@@ -340,9 +378,9 @@ def test_declaratively_registered_generator_always_available():
     call.
     """
     clear_generator_registrations()
-    generator = generator_description('flow-dsl', 'plantuml')
+    generator = generator_description("flow-dsl", "plantuml")
 
-    assert generator.language == 'flow-dsl'
-    assert generator.target == 'PlantUML'
+    assert generator.language == "flow-dsl"
+    assert generator.target == "PlantUML"
     assert callable(generator.generator)
-    assert generator.project_name == 'flow-codegen'
+    assert generator.project_name == "flow-codegen"
diff --git a/tests/functional/registration/test_version.py b/tests/functional/registration/test_version.py
index 126e852e5..27a691597 100644
--- a/tests/functional/registration/test_version.py
+++ b/tests/functional/registration/test_version.py
@@ -1,16 +1,17 @@
 """
 Test `version` command.
 """
-from pkg_resources import parse_version
-from textx.cli import textx
 from click.testing import CliRunner
+from packaging.version import parse
+
+from textx.cli import textx
 
 
 def test_version_command():
     runner = CliRunner()
-    result = runner.invoke(textx, ['version'])
+    result = runner.invoke(textx, ["version"])
     assert result.exit_code == 0
-    assert result.output.startswith('textX')
+    assert result.output.startswith("textX")
     version_text = result.output.split()[-1]
-    version = parse_version(version_text)
-    assert version.__class__.__name__ == 'Version'
+    version = parse(version_text)
+    assert version.__class__.__name__ == "Version"
diff --git a/tests/functional/regressions/issue323/test_issue323_backslash_chars.py b/tests/functional/regressions/issue323/test_issue323_backslash_chars.py
index ae3412adb..b7ea0ba8f 100644
--- a/tests/functional/regressions/issue323/test_issue323_backslash_chars.py
+++ b/tests/functional/regressions/issue323/test_issue323_backslash_chars.py
@@ -1,35 +1,39 @@
-from os.path import dirname, abspath, join
-from textx import metamodel_from_str, metamodel_from_file
+from os.path import abspath, dirname, join
+
+from textx import metamodel_from_file, metamodel_from_str
 
 grammars = [
     # String match with backslash chars
-    r'''
+    r"""
     Rule1[noskipws]: 'First' '\n' Rule2;
     Rule2: / */- 'Second' '\t' 'Third';
-    ''',
+    """,
     # Regex match with backslash chars
-    r'''
+    r"""
     Rule1[noskipws]: 'First' /\n/ Rule2;
     Rule2: / */- 'Second' /\t/ 'Third';
-    '''
+    """,
 ]
 
 
 def test_newline_str_match_in_raw_str():
     for grammar in grammars:
         mm = metamodel_from_str(grammar)
-        model = mm.model_from_str('''First
-            Second\tThird''')
+        model = mm.model_from_str(
+            """First
+            Second\tThird"""
+        )
 
-        assert model == 'First\nSecond\tThird'
+        assert model == "First\nSecond\tThird"
 
 
 def test_newline_str_match_in_files():
     this_folder = dirname(abspath(__file__))
     for grammar in range(1, 3):
-        mm = metamodel_from_file(join(this_folder,
-                                      f'grammar{grammar}.tx'))
-        model = mm.model_from_str('''First
-            Second\tThird''')
+        mm = metamodel_from_file(join(this_folder, f"grammar{grammar}.tx"))
+        model = mm.model_from_str(
+            """First
+            Second\tThird"""
+        )
 
-        assert model == 'First\nSecond\tThird'
+        assert model == "First\nSecond\tThird"
diff --git a/tests/functional/regressions/test_issue103_python_like_import.py b/tests/functional/regressions/test_issue103_python_like_import.py
index dbf25864b..9af28e696 100644
--- a/tests/functional/regressions/test_issue103_python_like_import.py
+++ b/tests/functional/regressions/test_issue103_python_like_import.py
@@ -1,9 +1,9 @@
-from __future__ import unicode_literals
+from os.path import abspath, dirname, join
 
-from textx import metamodel_from_str
-from os.path import dirname, abspath, join
-import textx.scoping.providers as scoping_providers
 from pytest import raises
+
+import textx.scoping.providers as scoping_providers
+from textx import metamodel_from_str
 from textx.exceptions import TextXSemanticError
 
 
@@ -12,7 +12,8 @@ def test_issue103_python_like_import():
     see issue 103 for a detailed error report
     """
 
-    mm = metamodel_from_str(r'''
+    mm = metamodel_from_str(
+        r"""
         Model:
                 imports*=Import
                 classes*=Class
@@ -23,25 +24,28 @@ def test_issue103_python_like_import():
         FQN: ID+['.'];
         Import: 'import' importURI=STRING;
         Comment: /#.*$/;
-        ''')
+        """
+    )
 
     def importURI_to_scope_name(import_obj):
         # this method is responsible to deduce the module name in the
         # language from the importURI string
         # e.g. here: import "file.ext" --> module name "file".
-        return import_obj.importURI.split('.')[0]
+        return import_obj.importURI.split(".")[0]
 
     mm.register_scope_providers(
-        {"*.*": scoping_providers.
-            FQNImportURI(importAs=True,
-                         importURI_to_scope_name=importURI_to_scope_name)})
+        {
+            "*.*": scoping_providers.FQNImportURI(
+                importAs=True, importURI_to_scope_name=importURI_to_scope_name
+            )
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
-    m = mm.model_from_file(
-        join(abspath(dirname(__file__)), "issue103", "main.mod"))
+    m = mm.model_from_file(join(abspath(dirname(__file__)), "issue103", "main.mod"))
 
     #################################
     # TEST MODEL
@@ -59,7 +63,8 @@ def test_issue103_imported_namedspaces():
     see issue 103 for a detailed error report
     """
 
-    mm = metamodel_from_str(r'''
+    mm = metamodel_from_str(
+        r"""
         Model:
                 imports*=Import
                 packages*=Package
@@ -73,36 +78,44 @@ def test_issue103_imported_namedspaces():
         FQN: ID+['.'];
         Import: 'import' importURI=STRING;
         Comment: /#.*$/;
-        ''')
+        """
+    )
 
     def importURI_to_scope_name(import_obj):
         # this method is responsible to deduce the module name in the
         # language from the importURI string
         # e.g. here: import "file.ext" --> module name "file".
-        return import_obj.importURI.split('.')[0]
+        return import_obj.importURI.split(".")[0]
 
     def custom_scope_redirection(obj):
         from textx import textx_isinstance
+
         if textx_isinstance(obj, mm["PackageRef"]):
             if obj.ref is None:
                 from textx.scoping import Postponed
+
                 return Postponed()
             return [obj.ref]
         else:
             return []
 
     mm.register_scope_providers(
-        {"*.*": scoping_providers.
-            FQNImportURI(importAs=True,
-                         importURI_to_scope_name=importURI_to_scope_name,
-                         scope_redirection_logic=custom_scope_redirection)})
+        {
+            "*.*": scoping_providers.FQNImportURI(
+                importAs=True,
+                importURI_to_scope_name=importURI_to_scope_name,
+                scope_redirection_logic=custom_scope_redirection,
+            )
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
     # first test
-    mm.model_from_str('''
+    mm.model_from_str(
+        """
         package p1 {
             package p2 {
                 class a {};
@@ -111,12 +124,13 @@ class a {};
         using p1.p2 as main
         var x = new p1.p2.a()
         var y = new main.a()
-    ''')
+    """
+    )
 
     # first test (negative example)
-    with raises(TextXSemanticError,
-                match=r'.*Unknown object "error.a".*'):
-        mm.model_from_str('''
+    with raises(TextXSemanticError, match=r'.*Unknown object "error.a".*'):
+        mm.model_from_str(
+            """
             package p1 {
                 package p2 {
                     class a {};
@@ -125,12 +139,13 @@ class a {};
             using p1.p2 as main
             var x = new p1.p2.a()
             var y = new error.a()
-        ''')
+        """
+        )
 
     # first test (negative example)
-    with raises(TextXSemanticError,
-                match=r'.*Unknown object "p1.error".*'):
-        mm.model_from_str('''
+    with raises(TextXSemanticError, match=r'.*Unknown object "p1.error".*'):
+        mm.model_from_str(
+            """
             package p1 {
                 package p2 {
                     class a {};
@@ -139,11 +154,13 @@ class a {};
             using p1.error as main
             var x = new p1.p2.a()
             var y = new main.a()
-        ''')
+        """
+        )
 
     # second test
     m = mm.model_from_file(
-        join(abspath(dirname(__file__)), "issue103", "main.packageRef"))
+        join(abspath(dirname(__file__)), "issue103", "main.packageRef")
+    )
 
     #################################
     # TEST MODEL
diff --git a/tests/functional/regressions/test_issue108_obj_proc_multifile.py b/tests/functional/regressions/test_issue108_obj_proc_multifile.py
index f33444b44..7e4bf1fc7 100644
--- a/tests/functional/regressions/test_issue108_obj_proc_multifile.py
+++ b/tests/functional/regressions/test_issue108_obj_proc_multifile.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
+from os.path import dirname, join
+
 import textx
-from os.path import join, dirname
 import textx.scoping.providers as scoping_providers
 
 
@@ -9,13 +9,15 @@ def test_issue108_obj_proc_multifile():
     see issue 108 for a detailed error report
     """
 
-    mm = textx.metamodel_from_str('''
+    mm = textx.metamodel_from_str(
+        """
         Model:
           imports*=Import
           classes*=Class;
         Import: 'import' importURI=STRING ';';
         Class: 'class' name=ID '{' '}';
-    ''')
+    """
+    )
 
     lst_class_names = []
     lst_models = []
@@ -26,13 +28,12 @@ def print_obj(x):
     def print_model(m, mm):
         lst_models.append(m)
 
-    mm.register_scope_providers(
-        {'*.*': scoping_providers.PlainNameImportURI()})
-    mm.register_obj_processors({'Class': print_obj})
+    mm.register_scope_providers({"*.*": scoping_providers.PlainNameImportURI()})
+    mm.register_obj_processors({"Class": print_obj})
     mm.register_model_processor(print_model)
 
     current_dir = dirname(__file__)
-    mm.model_from_file(join(current_dir, 'issue108', 'a.dsl'))
+    mm.model_from_file(join(current_dir, "issue108", "a.dsl"))
 
-    assert 2 == len(lst_models)
-    assert 2 == len(lst_class_names)
+    assert len(lst_models) == 2
+    assert len(lst_class_names) == 2
diff --git a/tests/functional/regressions/test_issue128.py b/tests/functional/regressions/test_issue128.py
index 29d3793ea..d4b123dd8 100644
--- a/tests/functional/regressions/test_issue128.py
+++ b/tests/functional/regressions/test_issue128.py
@@ -4,23 +4,14 @@
 See https://github.com/textX/textX/pull/128
 
 """
-from __future__ import unicode_literals
-import sys
-from textx import metamodel_from_str
-from textx import textx_isinstance
-
-if sys.version < '3':
-    text = unicode  # noqa
-else:
-    text = str
+from textx import metamodel_from_str, textx_isinstance
 
 # Global variable namespace
 namespace = {}
 
 
 def test_abstract_alternative_string_match():
-
-    grammar = r'''
+    grammar = r"""
     Calc: assignments*=Assignment expression=Expression;
     Assignment: variable=ID '=' expression=Expression ';';
     Expression: operands=Term (operators=PlusOrMinus operands=Term)*;
@@ -30,15 +21,15 @@ def test_abstract_alternative_string_match():
     Factor: (sign=PlusOrMinus)?  op=Operand;
     PrimitiveOperand: op_num=NUMBER | op_id=ID;
     Operand: PrimitiveOperand | ('(' Expression ')');
-    '''
+    """
 
     calc_mm = metamodel_from_str(grammar)
 
-    input_expr = '''
+    input_expr = """
         a = 10;
         b = 2 * a + 17;
         -(4-1)*a+(2+4.67)+b*5.89/(.2+7)
-    '''
+    """
 
     model = calc_mm.model_from_str(input_expr)
 
@@ -46,63 +37,59 @@ def _is(x, rule):
         return textx_isinstance(x, calc_mm[rule])
 
     def assertIs(x, rule):
-        assert _is(x, rule), 'Unexpected object "{}" to rule "{}"'\
-            .format(x, rule)
+        assert _is(x, rule), f'Unexpected object "{x}" to rule "{rule}"'
 
     def evaluate(x):
-
         if isinstance(x, float):
             return x
 
-        elif _is(x, 'Expression'):
+        elif _is(x, "Expression"):
             ret = evaluate(x.operands[0])
 
-            for operator, operand in zip(x.operators,
-                                         x.operands[1:]):
-                if operator == '+':
+            for operator, operand in zip(x.operators, x.operands[1:]):
+                if operator == "+":
                     ret += evaluate(operand)
                 else:
                     ret -= evaluate(operand)
             return ret
 
-        elif _is(x, 'Term'):
+        elif _is(x, "Term"):
             ret = evaluate(x.operands[0])
 
-            for operator, operand in zip(x.operators,
-                                         x.operands[1:]):
-                if operator == '*':
+            for operator, operand in zip(x.operators, x.operands[1:]):
+                if operator == "*":
                     ret *= evaluate(operand)
                 else:
                     ret /= evaluate(operand)
             return ret
 
-        elif _is(x, 'Factor'):
+        elif _is(x, "Factor"):
             value = evaluate(x.op)
-            return -value if x.sign == '-' else value
+            return -value if x.sign == "-" else value
 
-        elif _is(x, 'Operand'):
-            if _is(x, 'PrimitiveOperand'):
+        elif _is(x, "Operand"):
+            if _is(x, "PrimitiveOperand"):
                 if x.op_num is not None:
                     return x.op_num
                 elif x.op_id:
                     if x.op_id in namespace:
                         return namespace[x.op_id]
                     else:
-                        raise Exception('Unknown variable "{}" at position {}'
-                                        .format(x.op_id, x._tx_position))
+                        raise Exception(
+                            f'Unknown variable "{x.op_id}" at position {x._tx_position}'
+                        )
             else:
-                assertIs(x, 'CompoundOperand')
+                assertIs(x, "CompoundOperand")
                 return evaluate(x.expression)
 
-        elif _is(x, 'Calc'):
+        elif _is(x, "Calc"):
             for a in x.assignments:
                 namespace[a.variable] = evaluate(a.expression)
 
             return evaluate(x.expression)
 
         else:
-            assert False, 'Unexpected object "{}" of type "{}"'\
-                .format(x, type(x))
+            raise AssertionError(f'Unexpected object "{x}" of type "{type(x)}"')
 
     result = evaluate(model)
 
@@ -118,18 +105,18 @@ def test_abstract_alternative_multiple_rules_raises_exception():
     # In this grammar A is an abstract rule and referencing C D from second
     # alternative would yield just the first common rule object (C in this
     # case).
-    grammar = r'''
+    grammar = r"""
     Model: a+=A;
     A: B | '(' C D ')';
     B: 'B' name=ID x=INT;
     C: 'C' name=ID;
     D: 'D' x=INT;
-    '''
+    """
 
     meta = metamodel_from_str(grammar)
-    model = meta.model_from_str('B somename 23 ( C othername D 67 )')
+    model = meta.model_from_str("B somename 23 ( C othername D 67 )")
     assert len(model.a) == 2
-    assert model.a[0].name == 'somename'
+    assert model.a[0].name == "somename"
     assert model.a[0].x == 23
-    assert type(model.a[1]).__name__ == 'C'
-    assert model.a[1].name == 'othername'
+    assert type(model.a[1]).__name__ == "C"
+    assert model.a[1].name == "othername"
diff --git a/tests/functional/regressions/test_issue140.py b/tests/functional/regressions/test_issue140.py
index b080e349c..5334cd655 100644
--- a/tests/functional/regressions/test_issue140.py
+++ b/tests/functional/regressions/test_issue140.py
@@ -1,15 +1,14 @@
-from __future__ import unicode_literals
 import os
 import os.path
-from textx import (metamodel_from_file,
-                   register_language, clear_language_registrations)
-import textx.scoping.providers as scoping_providers
+
 import textx.scoping as scoping
+import textx.scoping.providers as scoping_providers
+from textx import clear_language_registrations, metamodel_from_file, register_language
 
 
-class C1(object):
+class C1:
     def __init__(self, **kwargs):
-        for k in kwargs.keys():
+        for k in kwargs:
             setattr(self, k, kwargs[k])
 
 
@@ -18,16 +17,16 @@ def test_multi_metamodel_obj_proc():
     repo = scoping_providers.PlainNameGlobalRepo()
     repo.register_models(os.path.dirname(__file__) + "/issue140/*.a")
 
-    mm_A = metamodel_from_file(os.path.join(
-        os.path.dirname(__file__),
-        "issue140",
-        "A.tx"
-    ), global_repository=global_repo, classes=[C1])
-    mm_B = metamodel_from_file(os.path.join(
-        os.path.dirname(__file__),
-        "issue140",
-        "B.tx"
-    ), global_repository=global_repo, classes=[C1])
+    mm_A = metamodel_from_file(
+        os.path.join(os.path.dirname(__file__), "issue140", "A.tx"),
+        global_repository=global_repo,
+        classes=[C1],
+    )
+    mm_B = metamodel_from_file(
+        os.path.join(os.path.dirname(__file__), "issue140", "B.tx"),
+        global_repository=global_repo,
+        classes=[C1],
+    )
 
     mm_B.register_scope_providers({"*.*": repo})
 
@@ -38,19 +37,7 @@ def proc(a):
     mm_B.register_obj_processors({"C1": proc})
 
     clear_language_registrations()
-    register_language(
-        'a-dsl',
-        pattern='*.a',
-        description='Test Lang A',
-        metamodel=mm_A)
-    register_language(
-        'b-dsl',
-        pattern='*.b',
-        description='Test Lang B',
-        metamodel=mm_B)
-
-    mm_B.model_from_file(os.path.join(
-        os.path.dirname(__file__),
-        "issue140",
-        "b.b"
-    ))
+    register_language("a-dsl", pattern="*.a", description="Test Lang A", metamodel=mm_A)
+    register_language("b-dsl", pattern="*.b", description="Test Lang B", metamodel=mm_B)
+
+    mm_B.model_from_file(os.path.join(os.path.dirname(__file__), "issue140", "b.b"))
diff --git a/tests/functional/regressions/test_issue14_obj_proc.py b/tests/functional/regressions/test_issue14_obj_proc.py
index 091d02a46..9f1c6174f 100644
--- a/tests/functional/regressions/test_issue14_obj_proc.py
+++ b/tests/functional/regressions/test_issue14_obj_proc.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 
 from textx.metamodel import metamodel_from_str
@@ -32,10 +31,10 @@ def test_issue_14():
     # note: you can also use "STRICTFLOAT | INT" instead of "NUMBER"
 
     mm = metamodel_from_str(grammar)
-    metamodel_export(mm, 'test_issue_14_metamodel.dot')
+    metamodel_export(mm, "test_issue_14_metamodel.dot")
 
     # Error happens only when there are obj. processors registered
-    mm.register_obj_processors({'InitialCommand': lambda x: x})
+    mm.register_obj_processors({"InitialCommand": lambda x: x})
 
     model_str = """
         begin
@@ -48,10 +47,10 @@ def test_issue_14():
         end
     """
     model = mm.model_from_str(model_str)
-    model_export(model, 'test_issue_14_model.dot')
+    model_export(model, "test_issue_14_model.dot")
     assert model.commands[0].x == 2
     assert model.commands[0].y == 3
-    assert model.commands[1] == 'sleep'
+    assert model.commands[1] == "sleep"
     assert model.commands[2] == 34
     assert model.commands[3] == 4.3
     assert model.commands[4] is True
diff --git a/tests/functional/regressions/test_issue155.py b/tests/functional/regressions/test_issue155.py
index aad4b96e9..837225a7c 100644
--- a/tests/functional/regressions/test_issue155.py
+++ b/tests/functional/regressions/test_issue155.py
@@ -1,10 +1,8 @@
-from __future__ import unicode_literals
-
-
 def test_textx_issue155():
     import textx
 
-    METAMODEL = textx.metamodel_from_str("""
+    METAMODEL = textx.metamodel_from_str(
+        """
     Model: expression=Expr ;
 
     ParenExpr: '(' Expr ')';
@@ -18,29 +16,32 @@ def test_textx_issue155():
     Identifier: id=/[a-zA-Z0-9_-]+/;
     MathOperator:  op=/=|>|</;
     BoolOperator:  op=/AND|OR/;
-    """)
+    """
+    )
 
     from textx import textx_isinstance
 
-    m1 = METAMODEL.model_from_str('(b=3 OR c=4)')
-    assert textx_isinstance(m1.expression, METAMODEL['BaseExpr'])
-    assert textx_isinstance(m1.expression.left, METAMODEL['RawCondition'])
+    m1 = METAMODEL.model_from_str("(b=3 OR c=4)")
+    assert textx_isinstance(m1.expression, METAMODEL["BaseExpr"])
+    assert textx_isinstance(m1.expression.left, METAMODEL["RawCondition"])
     assert len(m1.expression.operations) == 1
-    assert m1.expression.operations[0].op.op == 'OR'
-    assert textx_isinstance(m1.expression.operations[0].remaining,
-                            METAMODEL['RawCondition'])
-
-    m2 = METAMODEL.model_from_str('a=2 AND (b=3 OR c=4)')
-    assert textx_isinstance(m2.expression, METAMODEL['BaseExpr'])
-    assert textx_isinstance(m2.expression.left, METAMODEL['RawCondition'])
+    assert m1.expression.operations[0].op.op == "OR"
+    assert textx_isinstance(
+        m1.expression.operations[0].remaining, METAMODEL["RawCondition"]
+    )
+
+    m2 = METAMODEL.model_from_str("a=2 AND (b=3 OR c=4)")
+    assert textx_isinstance(m2.expression, METAMODEL["BaseExpr"])
+    assert textx_isinstance(m2.expression.left, METAMODEL["RawCondition"])
     assert len(m2.expression.operations) == 1
-    assert m2.expression.operations[0].op.op == 'AND'
-    assert textx_isinstance(m2.expression.operations[0].remaining,
-                            METAMODEL['BaseExpr'])
-    assert textx_isinstance(m2.expression.operations[0].remaining.left,
-                            METAMODEL['RawCondition'])
+    assert m2.expression.operations[0].op.op == "AND"
+    assert textx_isinstance(m2.expression.operations[0].remaining, METAMODEL["BaseExpr"])
+    assert textx_isinstance(
+        m2.expression.operations[0].remaining.left, METAMODEL["RawCondition"]
+    )
     assert len(m2.expression.operations[0].remaining.operations) == 1
-    assert m2.expression.operations[0].remaining.operations[0].op.op == 'OR'
-    assert textx_isinstance(m2.expression.operations[0].remaining.
-                            operations[0].remaining,
-                            METAMODEL['RawCondition'])
+    assert m2.expression.operations[0].remaining.operations[0].op.op == "OR"
+    assert textx_isinstance(
+        m2.expression.operations[0].remaining.operations[0].remaining,
+        METAMODEL["RawCondition"],
+    )
diff --git a/tests/functional/regressions/test_issue166.py b/tests/functional/regressions/test_issue166.py
index 0d14b512a..ac3760928 100644
--- a/tests/functional/regressions/test_issue166.py
+++ b/tests/functional/regressions/test_issue166.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 import textx
 
 
@@ -22,6 +21,6 @@ def test_issue166_wrong_multiple_rule_reference():
 
     metamodel = textx.metamodel_from_str(grammar)
     assert metamodel
-    model = metamodel.model_from_str('require a. group 4')
-    assert model.commands[0] == 'requirea'
-    assert model.commands[1].hello == '4'
+    model = metamodel.model_from_str("require a. group 4")
+    assert model.commands[0] == "requirea"
+    assert model.commands[1].hello == "4"
diff --git a/tests/functional/regressions/test_issue188.py b/tests/functional/regressions/test_issue188.py
index f17ad0abf..d15343a8a 100644
--- a/tests/functional/regressions/test_issue188.py
+++ b/tests/functional/regressions/test_issue188.py
@@ -1,44 +1,55 @@
-from __future__ import unicode_literals
 import pytest
-from textx import metamodel_from_str, TextXSyntaxError
+
+from textx import TextXSyntaxError, metamodel_from_str
 
 
 def test_issue188_skipws_1():
-    mm = metamodel_from_str(r'''
+    mm = metamodel_from_str(
+        r"""
         File: 'foo' /\s/ 'bar';
-    ''', skipws=False)
-    mm.model_from_str('foo bar')
+    """,
+        skipws=False,
+    )
+    mm.model_from_str("foo bar")
 
 
 def test_issue188_skipws_2():
-    mm = metamodel_from_str('''
+    mm = metamodel_from_str(
+        """
         File: 'foo' ' ' 'bar';
-    ''', skipws=False)
-    mm.model_from_str('foo bar')
+    """,
+        skipws=False,
+    )
+    mm.model_from_str("foo bar")
 
 
 def test_issue188_skipws_3():
-    mm = metamodel_from_str(r'''
+    mm = metamodel_from_str(
+        r"""
         File[noskipws]: 'foo' SPACE 'bar';
         SPACE[noskipws]: /\s/;
-    ''')
-    mm.model_from_str('foo bar')
+    """
+    )
+    mm.model_from_str("foo bar")
 
 
 def test_issue188_skipws_4():
-    mm = metamodel_from_str('''
+    mm = metamodel_from_str(
+        """
         File[noskipws]: 'foo' SPACE 'bar';
         SPACE[noskipws]: ' ';
-    ''')
-    mm.model_from_str('foo bar')
+    """
+    )
+    mm.model_from_str("foo bar")
 
 
 def test_issue188_skipws_5():
-    mm = metamodel_from_str('''
+    mm = metamodel_from_str(
+        """
         File[noskipws]: ' foo' SPACE 'bar ';
         SPACE[noskipws]: ' ';
-    ''')
-    with pytest.raises(TextXSyntaxError,
-                       match=r'.*\d+: Expected \'bar \''):
-        mm.model_from_str(' foo bar')
-    mm.model_from_str(' foo bar ')
+    """
+    )
+    with pytest.raises(TextXSyntaxError, match=r".*\d+: Expected \'bar \'"):
+        mm.model_from_str(" foo bar")
+    mm.model_from_str(" foo bar ")
diff --git a/tests/functional/regressions/test_issue205_skipws_propagation.py b/tests/functional/regressions/test_issue205_skipws_propagation.py
index ad7add9a2..491f79e3c 100644
--- a/tests/functional/regressions/test_issue205_skipws_propagation.py
+++ b/tests/functional/regressions/test_issue205_skipws_propagation.py
@@ -1,20 +1,21 @@
-from __future__ import unicode_literals
-
 import pytest
-from textx import metamodel_from_str, TextXSyntaxError
+
+from textx import TextXSyntaxError, metamodel_from_str
 
 
 def test_issue205_skipws_propagation():
     """
     Test a problem found in Arpeggio not maintaining `skipws/noskipws` applied
     to ordered choice root rule.
-    Reported at SO: https://stackoverflow.com/questions/57944531/how-do-you-correctly-mix-textx-skipws-non-skipws  # noqa
-    """
+    Reported at SO: https://stackoverflow.com/questions/57944531/how-do-you-correctly-mix-textx-skipws-non-skipws
+    """  # noqa: E501
 
-    mm = metamodel_from_str(r'''
+    mm = metamodel_from_str(
+        r"""
             Sentence[skipws]: words*=Word;
             Word[noskipws]: ID '.' | ID | '.';
-        ''')
+        """
+    )
 
     # If ```noskipws``` from the ```Word``` rule is obeyed then this won't
     # parse as there is no rule to consume spaces in between the words. In the
@@ -22,7 +23,7 @@ def test_issue205_skipws_propagation():
     # ```noskipws``` from Word so the parse will not fail but it would not be
     # what should be expected.
     with pytest.raises(TextXSyntaxError):
-        mm.model_from_str('''foo bar .''')
+        mm.model_from_str("""foo bar .""")
 
     # We should consume spaces explicitly. Here, we show that a rule with the
     # ```noskipws``` rule modifier is able to consume spaces explicitly. Match
@@ -32,10 +33,12 @@ def test_issue205_skipws_propagation():
     # words, because the rule modifiers
     # (http://textx.github.io/textX/stable/grammar/#rule-modifiers) apply
     # immediately. Thus, we need to consume the extra spaces before a word.
-    mm = metamodel_from_str(r'''
+    mm = metamodel_from_str(
+        r"""
             Sentence[skipws]: words*=Word;
             Word[noskipws]: /\s*/- (ID '.' | ID | '.');
-        ''')
+        """
+    )
 
-    m = mm.model_from_str('''foo bar .''')
+    m = mm.model_from_str("""foo bar .""")
     assert len(m.words) == 3
diff --git a/tests/functional/regressions/test_issue206_regex.py b/tests/functional/regressions/test_issue206_regex.py
index 50092f732..669a121db 100644
--- a/tests/functional/regressions/test_issue206_regex.py
+++ b/tests/functional/regressions/test_issue206_regex.py
@@ -1,20 +1,27 @@
-from __future__ import unicode_literals
 from textx.metamodel import metamodel_from_str
 
 
 def xtest_issue206_regex_reference1():
-    mm = metamodel_from_str('''
+    mm = metamodel_from_str(
+        """
         Word: ('bar' /[ ]*/)*;
-    ''', skipws=False, debug=False)
+    """,
+        skipws=False,
+        debug=False,
+    )
 
-    m = mm.model_from_str('''bar bar''', debug=False)
+    m = mm.model_from_str("""bar bar""", debug=False)
     assert m is not None
 
 
 def test_issue206_regex():
-    mm = metamodel_from_str('''
+    mm = metamodel_from_str(
+        """
         Word: ('bar' / */)*;
-    ''', skipws=False, debug=False)
+    """,
+        skipws=False,
+        debug=False,
+    )
 
-    m = mm.model_from_str('''bar bar''', debug=False)
+    m = mm.model_from_str("""bar bar""", debug=False)
     assert m is not None
diff --git a/tests/functional/regressions/test_issue23_direct_recursion.py b/tests/functional/regressions/test_issue23_direct_recursion.py
index f2976817d..659adc696 100644
--- a/tests/functional/regressions/test_issue23_direct_recursion.py
+++ b/tests/functional/regressions/test_issue23_direct_recursion.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 
 from textx.metamodel import metamodel_from_str
@@ -16,9 +15,9 @@ def test_issue_23():
     """
     mm = metamodel_from_str(grammar)
 
-    assert mm['List']._tx_type is RULE_COMMON
-    assert mm['Value']._tx_type is RULE_ABSTRACT
-    assert mm['Value']._tx_inh_by == [mm['List']]
+    assert mm["List"]._tx_type is RULE_COMMON
+    assert mm["Value"]._tx_type is RULE_ABSTRACT
+    assert mm["Value"]._tx_inh_by == [mm["List"]]
 
     grammar = """
     List: '{' members+=Value '}';
@@ -26,8 +25,8 @@ def test_issue_23():
     """
     mm = metamodel_from_str(grammar)
 
-    assert mm['List']._tx_type is RULE_COMMON
-    assert mm['Value']._tx_type is RULE_MATCH
+    assert mm["List"]._tx_type is RULE_COMMON
+    assert mm["Value"]._tx_type is RULE_MATCH
 
     grammar = """
     ListSyntax: '{' List '}';
@@ -36,8 +35,8 @@ def test_issue_23():
     """
     mm = metamodel_from_str(grammar)
 
-    assert mm['ListSyntax']._tx_type is RULE_ABSTRACT
-    assert mm['ListSyntax']._tx_inh_by == [mm['List']]
-    assert mm['List']._tx_type is RULE_COMMON
-    assert mm['Value']._tx_type is RULE_ABSTRACT
-    assert mm['Value']._tx_inh_by == [mm['ListSyntax']]
+    assert mm["ListSyntax"]._tx_type is RULE_ABSTRACT
+    assert mm["ListSyntax"]._tx_inh_by == [mm["List"]]
+    assert mm["List"]._tx_type is RULE_COMMON
+    assert mm["Value"]._tx_type is RULE_ABSTRACT
+    assert mm["Value"]._tx_inh_by == [mm["ListSyntax"]]
diff --git a/tests/functional/regressions/test_issue246.py b/tests/functional/regressions/test_issue246.py
index 6807d0113..c4341191d 100644
--- a/tests/functional/regressions/test_issue246.py
+++ b/tests/functional/regressions/test_issue246.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 from textx import metamodel_from_str
 
 
@@ -8,7 +7,7 @@ def test_multiple_rule_modifiers():
     See: https://github.com/textX/textX/issues/246
     """
 
-    grammar = r'''
+    grammar = r"""
     main_rule[ws=' \t']:
         "word1" rule1
     ;
@@ -16,11 +15,11 @@ def test_multiple_rule_modifiers():
     rule1[skipws, ws=' \t\n']:
         "word2" "word3"
     ;
-    '''
+    """
 
     meta = metamodel_from_str(grammar)
-    s = '''word1 word2
-    word3'''
+    s = """word1 word2
+    word3"""
 
     model = meta.model_from_str(s)
-    assert model == 'word1word2word3'
+    assert model == "word1word2word3"
diff --git a/tests/functional/regressions/test_issue266.py b/tests/functional/regressions/test_issue266.py
new file mode 100644
index 000000000..8a4d91ae2
--- /dev/null
+++ b/tests/functional/regressions/test_issue266.py
@@ -0,0 +1,19 @@
+import pytest
+
+from textx import metamodel_from_str
+from textx.exceptions import TextXSemanticError
+
+
+def test_unhashable_type_for_name():
+    """
+    Test that using unhashable type for name of model object is correctly reported.
+    """
+
+    mm = metamodel_from_str(
+        """
+    TestObj: name+=ID['.'];
+    """
+    )
+
+    with pytest.raises(TextXSemanticError, match=".*of unhashable type.*"):
+        mm.model_from_str("test")
diff --git a/tests/functional/regressions/test_issue275.py b/tests/functional/regressions/test_issue275.py
index 76af2a8dc..a5e43d83e 100644
--- a/tests/functional/regressions/test_issue275.py
+++ b/tests/functional/regressions/test_issue275.py
@@ -1,12 +1,13 @@
-from __future__ import unicode_literals
-from textx import metamodel_from_str
-from textx.export import metamodel_export_tofile, DotRenderer
 import io
+
 import html5lib
 
+from textx import metamodel_from_str
+from textx.export import DotRenderer, metamodel_export_tofile
+
 
 def test_issue275():
-    grammar = r'''
+    grammar = r"""
 
     Comment:
         "<:" ( !"*/" /./ )* ":>"
@@ -27,7 +28,7 @@ def test_issue275():
                                   // built-in entities registered on the meta-model
                                   // for primitive types (integer, string)
     ;
-    '''
+    """
 
     entity_mm = metamodel_from_str(grammar)
 
diff --git a/tests/functional/regressions/test_issue310.py b/tests/functional/regressions/test_issue310.py
index 6aac68adb..fb5638ef8 100644
--- a/tests/functional/regressions/test_issue310.py
+++ b/tests/functional/regressions/test_issue310.py
@@ -1,8 +1,6 @@
-from __future__ import unicode_literals
 from textx import metamodel_from_str
 
-
-grammar = '''
+grammar = """
 Calc: expression=Expression;
 Expression: op=Term (op=PlusOrMinus op=Term)* ;
 PlusOrMinus: '+' | '-';
@@ -10,10 +8,10 @@
 MulOrDiv: '*' | '/' ;
 Factor: (sign=PlusOrMinus)?  op=Operand;
 Operand: op=NUMBER | op=ID | ('(' op=Expression ')');
-'''
+"""
 
 
-class Calc(object):
+class Calc:
     def __init__(self, expression):
         # A simple reduction to trigger the #310 problem
         # Here, we reduce the expression as it is only a multiplication
@@ -30,5 +28,5 @@ def test_non_abstract_grammar_rule_different_from_obj_metaclass():
 
     """
     mm = metamodel_from_str(grammar, classes=[Calc])
-    model = mm.model_from_str('2 * 5')
-    assert model.expression.__class__.__name__ == 'Term'
+    model = mm.model_from_str("2 * 5")
+    assert model.expression.__class__.__name__ == "Term"
diff --git a/tests/functional/regressions/test_issue33.py b/tests/functional/regressions/test_issue33.py
index 4d780f383..a810587cd 100644
--- a/tests/functional/regressions/test_issue33.py
+++ b/tests/functional/regressions/test_issue33.py
@@ -1,10 +1,8 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 from textx.metamodel import metamodel_from_str
 
 
 def test_issue_33_parentheses():
-
     grammar = """
     Method:
         'func('  (params+=Parameter[','])?  ')'
@@ -21,11 +19,11 @@ def test_issue_33_parentheses():
         """
     )
     assert model
-    assert model.params[0].type == 'a'
-    assert model.params[0].name == 'b'
+    assert model.params[0].type == "a"
+    assert model.params[0].name == "b"
 
-    assert model.params[1].type == ''
-    assert model.params[1].name == 'c'
+    assert model.params[1].type == ""
+    assert model.params[1].name == "c"
 
 
 def test_issue_33_alternatives():
@@ -45,11 +43,11 @@ def test_issue_33_alternatives():
         """
     )
     assert model
-    assert model.params[0].type == 'a'
-    assert model.params[0].name == 'b'
+    assert model.params[0].type == "a"
+    assert model.params[0].name == "b"
 
-    assert model.params[1].type == ''
-    assert model.params[1].name == 'c'
+    assert model.params[1].type == ""
+    assert model.params[1].name == "c"
 
 
 def test_issue_33_alternatives_with_arbitrary_named_attr():
@@ -69,8 +67,8 @@ def test_issue_33_alternatives_with_arbitrary_named_attr():
         """
     )
     assert model
-    assert model.params[0].type == 'a'
-    assert model.params[0].my_name == 'b'
+    assert model.params[0].type == "a"
+    assert model.params[0].my_name == "b"
 
-    assert model.params[1].type == ''
-    assert model.params[1].my_name == 'c'
+    assert model.params[1].type == ""
+    assert model.params[1].my_name == "c"
diff --git a/tests/functional/regressions/test_issue358.py b/tests/functional/regressions/test_issue358.py
index bb2ee09cd..b5cdb58af 100644
--- a/tests/functional/regressions/test_issue358.py
+++ b/tests/functional/regressions/test_issue358.py
@@ -3,17 +3,21 @@
 Usage of ordered choice in unordered groups.
 """
 import pytest
-from textx import metamodel_from_str, TextXSyntaxError
 
+from textx import TextXSyntaxError, metamodel_from_str
 
-@pytest.mark.parametrize("grammar", [
-    r'''
+
+@pytest.mark.parametrize(
+    "grammar",
+    [
+        r"""
     Unordered: 'begin' (first?='first' second?='second')#;
-    ''',
-    r'''
+    """,
+        r"""
     Unordered: 'begin' (first?='first' | second?='second')#;
-    '''
-])
+    """,
+    ],
+)
 def test_unordered_group_sequence_choice(grammar):
     """
     Test the equivalence of using sequence and ordered choice in optional matches in
@@ -21,74 +25,80 @@ def test_unordered_group_sequence_choice(grammar):
     """
     mm = metamodel_from_str(grammar)
 
-    model = mm.model_from_str('begin second')
+    model = mm.model_from_str("begin second")
     assert model.second and not model.first
-    model = mm.model_from_str('begin first')
+    model = mm.model_from_str("begin first")
     assert model.first and not model.second
-    model = mm.model_from_str('begin first second')
+    model = mm.model_from_str("begin first second")
     assert model.first and model.second
-    model = mm.model_from_str('begin second first')
+    model = mm.model_from_str("begin second first")
     assert model.first and model.second
-    model = mm.model_from_str('begin')
+    model = mm.model_from_str("begin")
     assert not model.first and not model.second
 
     with pytest.raises(TextXSyntaxError):
-        mm.model_from_str('begin first second first')
+        mm.model_from_str("begin first second first")
 
     with pytest.raises(TextXSyntaxError):
-        mm.model_from_str('begin first first')
+        mm.model_from_str("begin first first")
 
 
-@pytest.mark.parametrize("grammar", [
-    r'''
+@pytest.mark.parametrize(
+    "grammar",
+    [
+        r"""
     Unordered: 'begin' (first='first' second='second')#;
-    ''',
-    r'''
+    """,
+        r"""
     Unordered: 'begin' (first='first' | second='second')#;
-    '''
-])
+    """,
+    ],
+)
 def test_unordered_group_sequence_choice_nonoptional(grammar):
     """
     Test the equivalence of using sequence and ordered choice in unordered groups.
     """
     mm = metamodel_from_str(grammar)
 
-    model = mm.model_from_str('begin first second')
-    assert model.first == 'first' and model.second == 'second'
-    model = mm.model_from_str('begin second first')
-    assert model.first == 'first' and model.second == 'second'
+    model = mm.model_from_str("begin first second")
+    assert model.first == "first" and model.second == "second"
+    model = mm.model_from_str("begin second first")
+    assert model.first == "first" and model.second == "second"
 
     with pytest.raises(TextXSyntaxError):
-        mm.model_from_str('begin first second first')
+        mm.model_from_str("begin first second first")
 
     with pytest.raises(TextXSyntaxError):
-        mm.model_from_str('begin first first')
+        mm.model_from_str("begin first first")
 
 
-@pytest.mark.parametrize("grammar", [
-    r'''
+@pytest.mark.parametrize(
+    "grammar",
+    [
+        r"""
     Unordered: 'begin' (first='first' second='second' | third='third')#;
-    ''',
-    r'''
+    """,
+        r"""
     Unordered: 'begin' ((first='first' second='second')  third='third')#;
-    '''
-])
+    """,
+    ],
+)
 def test_unordered_group_choice_with_sequences(grammar):
     """
     Test the equivalence of using ordered choice and a sequence of parenthesed groups.
     """
     mm = metamodel_from_str(grammar)
 
-    model = mm.model_from_str('begin first second third')
-    assert model.first == 'first' and model.second == 'second' and model.third == 'third'
-    model = mm.model_from_str('begin third first second')
-    assert model.first == 'first' and model.second == 'second' and model.third == 'third'
+    model = mm.model_from_str("begin first second third")
+    assert model.first == "first" and model.second == "second" and model.third == "third"
+    model = mm.model_from_str("begin third first second")
+    assert model.first == "first" and model.second == "second" and model.third == "third"
 
     with pytest.raises(TextXSyntaxError):
-        mm.model_from_str('begin second first third')
+        mm.model_from_str("begin second first third")
 
     with pytest.raises(TextXSyntaxError):
-        mm.model_from_str('begin third second first')
+        mm.model_from_str("begin third second first")
 
 
 def test_unordered_group_with_optional_assignments():
@@ -96,12 +106,12 @@ def test_unordered_group_with_optional_assignments():
     Test for a question raised during the code review:
     https://github.com/textX/textX/pull/359
     """
-    grammar = r'''
+    grammar = r"""
     Root: 'begin' a=Unordered;
     Unordered: (first?='first' second?='second' | third?='third')#;
-    '''
+    """
 
     mm = metamodel_from_str(grammar)
-    m = mm.model_from_str('begin')
+    m = mm.model_from_str("begin")
     # TODO: Maybe we shall have m.a not None and all its attributes to be None.
     assert m.a is None
diff --git a/tests/functional/regressions/test_issue384.py b/tests/functional/regressions/test_issue384.py
index 1e06df6ce..5d83f0b22 100644
--- a/tests/functional/regressions/test_issue384.py
+++ b/tests/functional/regressions/test_issue384.py
@@ -4,30 +4,33 @@
 
 
 def test_ref_with_separator():
-    grammar = r'''
+    grammar = r"""
     Model: objs+=Obj objrefs+=[Obj:ID];
     Obj: 'obj' name=ID;
-    '''
+    """
     mm = metamodel_from_str(grammar)
-    model = mm.model_from_str('''
+    model = mm.model_from_str(
+        """
     obj first
     obj second
 
     first second
-    ''')
+    """
+    )
 
     assert len(model.objrefs) == 2
-    assert model.objrefs[0].name == 'first'
+    assert model.objrefs[0].name == "first"
 
 
 def test_ref_with_separator_and_rrel():
-    grammar = r'''
+    grammar = r"""
     Model: objs+=Obj objrefs+=[Obj:Name|referenced] referenced+=Obj;
     Obj: 'obj' name=Name val=INT;
     Name: !'obj' ID;
-    '''
+    """
     mm = metamodel_from_str(grammar)
-    model = mm.model_from_str('''
+    model = mm.model_from_str(
+        """
     obj first 1
     obj second 2
 
@@ -35,10 +38,11 @@ def test_ref_with_separator_and_rrel():
 
     obj first 3
     obj second 4
-    ''')
+    """
+    )
 
     assert len(model.objrefs) == 2
-    assert model.objrefs[0].name == 'first' and model.objrefs[0].val == 3
+    assert model.objrefs[0].name == "first" and model.objrefs[0].val == 3
 
 
 def test_ref_with_separator_backward():
@@ -46,20 +50,22 @@ def test_ref_with_separator_backward():
     Same as above but with "|" as separator.
     TODO: Remove in version 4.0.
     """
-    grammar = r'''
+    grammar = r"""
     Model: objs+=Obj objrefs+=[Obj|ID];
     Obj: 'obj' name=ID;
-    '''
+    """
     mm = metamodel_from_str(grammar)
-    model = mm.model_from_str('''
+    model = mm.model_from_str(
+        """
     obj first
     obj second
 
     first second
-    ''')
+    """
+    )
 
     assert len(model.objrefs) == 2
-    assert model.objrefs[0].name == 'first'
+    assert model.objrefs[0].name == "first"
 
 
 def test_ref_with_separator_and_rrel_backward():
@@ -67,13 +73,14 @@ def test_ref_with_separator_and_rrel_backward():
     Same as above but with "|" as separator.
     TODO: Remove in version 4.0.
     """
-    grammar = r'''
+    grammar = r"""
     Model: objs+=Obj objrefs+=[Obj|Name|referenced] referenced+=Obj;
     Obj: 'obj' name=Name val=INT;
     Name: !'obj' ID;
-    '''
+    """
     mm = metamodel_from_str(grammar)
-    model = mm.model_from_str('''
+    model = mm.model_from_str(
+        """
     obj first 1
     obj second 2
 
@@ -81,7 +88,8 @@ def test_ref_with_separator_and_rrel_backward():
 
     obj first 3
     obj second 4
-    ''')
+    """
+    )
 
     assert len(model.objrefs) == 2
-    assert model.objrefs[0].name == 'first' and model.objrefs[0].val == 3
+    assert model.objrefs[0].name == "first" and model.objrefs[0].val == 3
diff --git a/tests/functional/regressions/test_issue72.py b/tests/functional/regressions/test_issue72.py
index ea9a13064..f5c0e8543 100644
--- a/tests/functional/regressions/test_issue72.py
+++ b/tests/functional/regressions/test_issue72.py
@@ -1,18 +1,11 @@
 """
 Testing model and object processors.
 """
-from __future__ import unicode_literals
 import pytest  # noqa
-import sys
 from textx.metamodel import metamodel_from_str
 
-if sys.version < '3':
-    text = unicode  # noqa
-else:
-    text = str
 
-
-class OuterObject(object):
+class OuterObject:
     counter = 0
 
     def __init__(self, parent=None, col_items=None):
@@ -24,7 +17,7 @@ def process(self):
         print("PROCESS OuterObject")
 
 
-class InnerObject(object):
+class InnerObject:
     counter = 0
 
     def __init__(self, parent=None, m_id=None):
@@ -69,16 +62,12 @@ def default_processor(obj):
 
 def parse_lola(grammar, lola_str):
     lola_str = lola_str
-    obj_processors = {
-        "InnerObject": default_processor,
-        "OuterObject": default_processor
-    }
+    obj_processors = {"InnerObject": default_processor, "OuterObject": default_processor}
 
     lola_classes = [InnerObject, OuterObject]
 
     meta_model = metamodel_from_str(
-        grammar, classes=lola_classes,
-        ignore_case=True, auto_init_attributes=False
+        grammar, classes=lola_classes, ignore_case=True, auto_init_attributes=False
     )
     meta_model.register_obj_processors(obj_processors)
     model = meta_model.model_from_str(lola_str)
diff --git a/tests/functional/regressions/test_issue78.py b/tests/functional/regressions/test_issue78.py
index 6bd7d1169..f6126fc9e 100644
--- a/tests/functional/regressions/test_issue78.py
+++ b/tests/functional/regressions/test_issue78.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 import textx
 
 
@@ -7,24 +6,30 @@ def test_issue78_obj_processors_base_attr_proc():
     Test works in 1.6.1 (rev 84bfd8748554237dd2a0919c45e761523a4e2712)
     Test fails in e1981fb74397dc53580e19919bdec95a5e55c7ee (1.7++)
     """
-    mm = textx.metamodel_from_str('''
+    mm = textx.metamodel_from_str(
+        """
         Model: base+=Base;
         Special1: '1' name=ID;
         Special2: '2' name=ID test=TestObj;
         Base: Special1|Special2;
         TestObj: name=ID;
-    ''')
+    """
+    )
     test_list = []
-    mm.register_obj_processors({
-        'TestObj': lambda o: test_list.append(o.name),
-    })
-    mm.model_from_str('''
+    mm.register_obj_processors(
+        {
+            "TestObj": lambda o: test_list.append(o.name),
+        }
+    )
+    mm.model_from_str(
+        """
     1 S1a
     1 S1b
     2 S2a t2a
     2 S2b t2b
-    ''')
-    assert ['t2a', 't2b'] == test_list
+    """
+    )
+    assert ["t2a", "t2b"] == test_list
 
 
 def test_issue78_obj_processors_order_of_eval():
@@ -32,22 +37,28 @@ def test_issue78_obj_processors_order_of_eval():
     Test shows that different processors can be assigned to Base and
     Specialized classes (in case of ABSTRACT_RULE)
     """
-    mm = textx.metamodel_from_str('''
+    mm = textx.metamodel_from_str(
+        """
         Model: base+=Base;
         Special1: '1' name=ID;
         Special2: '2' name=ID;
         Base: Special1|Special2;
-    ''')
+    """
+    )
     test_list = []
-    mm.register_obj_processors({
-        'Base': lambda o: test_list.append('Base_' + o.name),
-        'Special1': lambda o: test_list.append('Special_' + o.name),
-    })
-    mm.model_from_str('''
+    mm.register_obj_processors(
+        {
+            "Base": lambda o: test_list.append("Base_" + o.name),
+            "Special1": lambda o: test_list.append("Special_" + o.name),
+        }
+    )
+    mm.model_from_str(
+        """
     1 S1
     2 S2
-    ''')
-    assert ['Special_S1', 'Base_S1', 'Base_S2'] == test_list
+    """
+    )
+    assert ["Special_S1", "Base_S1", "Base_S2"] == test_list
 
 
 def test_issue78_obj_processors_replacement1_base():
@@ -55,23 +66,29 @@ def test_issue78_obj_processors_replacement1_base():
     Test shows that different processors can be assigned to Base and
     Specialized classes (in case of ABSTRACT_RULE)
     """
-    mm = textx.metamodel_from_str('''
+    mm = textx.metamodel_from_str(
+        """
         Model: base+=Base;
         Special1: '1' name=ID;
         Special2: '2' name=ID;
         Base: Special1|Special2;
-    ''')
+    """
+    )
     test_list = []
-    mm.register_obj_processors({
-        'Base': lambda o: 'Base_' + o.name,
-        'Special1': lambda o: test_list.append('Special_' + o.name),
-    })
-    m = mm.model_from_str('''
+    mm.register_obj_processors(
+        {
+            "Base": lambda o: "Base_" + o.name,
+            "Special1": lambda o: test_list.append("Special_" + o.name),
+        }
+    )
+    m = mm.model_from_str(
+        """
     1 S1
     2 S2
-    ''')
-    assert ['Special_S1'] == test_list
-    assert ['Base_S1', 'Base_S2'] == m.base
+    """
+    )
+    assert ["Special_S1"] == test_list
+    assert ["Base_S1", "Base_S2"] == m.base
 
 
 def test_issue78_obj_processors_replacement2_specialized():
@@ -79,25 +96,31 @@ def test_issue78_obj_processors_replacement2_specialized():
     Test shows that different processors can be assigned to Base and
     Specialized classes (in case of ABSTRACT_RULE)
     """
-    mm = textx.metamodel_from_str('''
+    mm = textx.metamodel_from_str(
+        """
         Model: base+=Base;
         Special1: '1' name=ID;
         Special2: '2' name=ID;
         Base: Special1|Special2;
-    ''')
+    """
+    )
     test_list = []
-    mm.register_obj_processors({
-        'Base': lambda o: test_list.append('Base_' + o.name),
-        'Special1': lambda o: 'Special_' + o.name,
-    })
-    m = mm.model_from_str('''
+    mm.register_obj_processors(
+        {
+            "Base": lambda o: test_list.append("Base_" + o.name),
+            "Special1": lambda o: "Special_" + o.name,
+        }
+    )
+    m = mm.model_from_str(
+        """
     1 S1
     2 S2
-    ''')
-    assert ['Base_S1', 'Base_S2'] == test_list
-    assert 'Special_S1' == m.base[0]
-    assert 'Special1' != m.base[0].__class__.__name__  # it is a str now...
-    assert 'Special2' == m.base[1].__class__.__name__  # this one is unchanged
+    """
+    )
+    assert ["Base_S1", "Base_S2"] == test_list
+    assert m.base[0] == "Special_S1"
+    assert m.base[0].__class__.__name__ != "Special1"  # it is a str now...
+    assert m.base[1].__class__.__name__ == "Special2"  # this one is unchanged
 
 
 def test_issue78_obj_processors_replacement_domination_of_specialized():
@@ -105,21 +128,27 @@ def test_issue78_obj_processors_replacement_domination_of_specialized():
     Test shows that different processors can be assigned to Base and
     Specialized classes (in case of ABSTRACT_RULE)
     """
-    mm = textx.metamodel_from_str('''
+    mm = textx.metamodel_from_str(
+        """
         Model: base+=Base;
         Special1: '1' name=ID;
         Special2: '2' name=ID;
         Base: Special1|Special2;
-    ''')
-    mm.register_obj_processors({
-        'Base': lambda o: 'Base_' + o.name,
-        'Special1': lambda o: 'Special_' + o.name,
-    })
-    m = mm.model_from_str('''
+    """
+    )
+    mm.register_obj_processors(
+        {
+            "Base": lambda o: "Base_" + o.name,
+            "Special1": lambda o: "Special_" + o.name,
+        }
+    )
+    m = mm.model_from_str(
+        """
     1 S1
     2 S2
-    ''')
-    assert ['Special_S1', 'Base_S2'] == m.base
+    """
+    )
+    assert ["Special_S1", "Base_S2"] == m.base
 
 
 def test_issue78_quickcheck_no_obj_processors_called_for_references():
@@ -135,23 +164,30 @@ def test_issue78_quickcheck_no_obj_processors_called_for_references():
     mm = textx.metamodel_from_str(grammarA)
 
     import textx.scoping.providers as scoping_providers
+
     global_repo_provider = scoping_providers.PlainNameGlobalRepo()
     mm.register_scope_providers({"*.*": global_repo_provider})
 
     test_list = []
-    mm.register_obj_processors({
-        'A': lambda o: test_list.append(o.name),
-    })
+    mm.register_obj_processors(
+        {
+            "A": lambda o: test_list.append(o.name),
+        }
+    )
 
     # no references to A: --> obj proc called
-    m1 = mm.model_from_str('''
+    m1 = mm.model_from_str(
+        """
     A a1 A a2 A a3
-    ''')
-    assert ['a1', 'a2', 'a3'] == test_list
+    """
+    )
+    assert ["a1", "a2", "a3"] == test_list
 
     # only references to A: --> obj proc not called
     global_repo_provider.add_model(m1)
-    mm.model_from_str('''
+    mm.model_from_str(
+        """
     B b1 -> a1 B b2 -> a2 B b3 -> a3
-    ''')
-    assert ['a1', 'a2', 'a3'] == test_list  # unchanged...
+    """
+    )
+    assert ["a1", "a2", "a3"] == test_list  # unchanged...
diff --git a/tests/functional/regressions/test_issue96.py b/tests/functional/regressions/test_issue96.py
index d3854a43c..e9f08e7f0 100644
--- a/tests/functional/regressions/test_issue96.py
+++ b/tests/functional/regressions/test_issue96.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 from textx import metamodel_from_str
 
 
@@ -23,7 +22,7 @@ def test_issue96_object_processor_on_match_rule_not_called():
     mycommand(0.1 1.1)
     """
     mm = metamodel_from_str(grammar)
-    mm.register_obj_processors({'ExplicitFloat': lambda x: float(x)})
+    mm.register_obj_processors({"ExplicitFloat": lambda x: float(x)})
 
     mymodel = mm.model_from_str(code)
 
@@ -32,7 +31,6 @@ def test_issue96_object_processor_on_match_rule_not_called():
 
 
 def test_issue96_object_processor_on_multiple_alternatives_not_called():
-
     grammar = r"""
     Command:
         name=ID parameters=ParameterList
@@ -56,9 +54,9 @@ def test_issue96_object_processor_on_multiple_alternatives_not_called():
     """
     mm = metamodel_from_str(grammar)
 
-    mm.register_obj_processors({
-        'Parameter': lambda x: x.value,
-        'ExplicitFloat': lambda x: float(x)})
+    mm.register_obj_processors(
+        {"Parameter": lambda x: x.value, "ExplicitFloat": lambda x: float(x)}
+    )
 
     mymodel = mm.model_from_str(code)
 
diff --git a/tests/functional/regressions/test_issue97.py b/tests/functional/regressions/test_issue97.py
index 3d4ecf04f..2c604b9dc 100644
--- a/tests/functional/regressions/test_issue97.py
+++ b/tests/functional/regressions/test_issue97.py
@@ -1,22 +1,26 @@
-from __future__ import unicode_literals
-import textx
 from pytest import raises
 
+import textx
+
 
 def test_issue97_buildin_NUMBER_as_INT_or_FLOAT():
     """
     see issue 97 for a detailed error report
     """
 
-    mm = textx.metamodel_from_str('''
+    mm = textx.metamodel_from_str(
+        """
         Model:
           value1=NUMBER value2=NUMBER
         ;
-    ''')
+    """
+    )
 
-    m = mm.model_from_str('''
+    m = mm.model_from_str(
+        """
         10 1.5
-    ''')
+    """
+    )
 
     assert isinstance(m.value1, int)
     assert isinstance(m.value2, float)
@@ -29,41 +33,53 @@ def test_issue97_buildin_STRICTFLOAT_as_INT_or_FLOAT():
     see issue 97 for a detailed error report
     """
 
-    mm = textx.metamodel_from_str('''
+    mm = textx.metamodel_from_str(
+        """
         Model: data+=Data;
         Data: StrictFloat|Float|Int;
         StrictFloat: 's' sfvalue=STRICTFLOAT;
         Float: 'f' fvalue=FLOAT;
         Int: 'i' ivalue=INT;
-    ''')
+    """
+    )
 
-    mm.model_from_str('''
+    mm.model_from_str(
+        """
         i1 i2 i3 f1 f2 f3 f1.0 f1. f.1 f1e1
-    ''')
+    """
+    )
 
-    mm.model_from_str('''
+    mm.model_from_str(
+        """
         s1.0 s1. s.1 s1e1
         s-1.0 s-1. s-.1 s-1e1
         s-1e-1 s-1.e-1 s-.1e-1
-    ''')
+    """
+    )
 
-    with raises(textx.exceptions.TextXSyntaxError,
-                match=r'.*Expected STRICTFLOAT.*'):
-        mm.model_from_str('''
+    with raises(textx.exceptions.TextXSyntaxError, match=r".*Expected STRICTFLOAT.*"):
+        mm.model_from_str(
+            """
             s1
-        ''')
-    with raises(textx.exceptions.TextXSyntaxError,
-                match=r'.*Expected INT.*'):
-        mm.model_from_str('''
+        """
+        )
+    with raises(textx.exceptions.TextXSyntaxError, match=r".*Expected INT.*"):
+        mm.model_from_str(
+            """
             i.1
-        ''')
-    with raises(textx.exceptions.TextXSyntaxError,
-                match=r'.*'):  # "i1" is parsed, the ".1" causes an error
-        mm.model_from_str('''
+        """
+        )
+    with raises(
+        textx.exceptions.TextXSyntaxError, match=r".*"
+    ):  # "i1" is parsed, the ".1" causes an error
+        mm.model_from_str(
+            """
             i1.1
-        ''')
-    with raises(textx.exceptions.TextXSyntaxError,
-                match=r'.*'):
-        mm.model_from_str('''
+        """
+        )
+    with raises(textx.exceptions.TextXSyntaxError, match=r".*"):
+        mm.model_from_str(
+            """
             i1e1
-        ''')
+        """
+        )
diff --git a/tests/functional/regressions/test_issue_34_resolving.py b/tests/functional/regressions/test_issue_34_resolving.py
index aa31dd428..d35477ac2 100644
--- a/tests/functional/regressions/test_issue_34_resolving.py
+++ b/tests/functional/regressions/test_issue_34_resolving.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 from textx.metamodel import metamodel_from_str
 
@@ -44,7 +43,6 @@ def test_issue_34_resolving():
     """
 
     meta_model = metamodel_from_str(grammar)
-    model = meta_model.model_from_str(
-        "attr_123, attr_444 formula attr_123 < 'aa'")
-    assert type(model.form.value.values[0].attribute).__name__ == 'Attribute'
-    assert model.form.value.values[0].attribute.name == 'attr_123'
+    model = meta_model.model_from_str("attr_123, attr_444 formula attr_123 < 'aa'")
+    assert type(model.form.value.values[0].attribute).__name__ == "Attribute"
+    assert model.form.value.values[0].attribute.name == "attr_123"
diff --git a/tests/functional/regressions/test_issue_53_comments.py b/tests/functional/regressions/test_issue_53_comments.py
index 6ee6d91db..a69842931 100644
--- a/tests/functional/regressions/test_issue_53_comments.py
+++ b/tests/functional/regressions/test_issue_53_comments.py
@@ -1,13 +1,12 @@
-from __future__ import unicode_literals
 from textx import metamodel_from_str
 
 
 def test_comments_skipws_optimization():
-    '''
+    """
     See https://github.com/textX/textX/issues/53
-    '''
+    """
 
-    grammar = r'''
+    grammar = r"""
     file:
         lines*=cascading /\s*/
     ;
@@ -33,41 +32,41 @@ def test_comments_skipws_optimization():
     group:
         keyword=ID '{' entries*=cascading '}'
     ;
-    '''
+    """
 
     mm = metamodel_from_str(grammar)
 
-    model_str = r'''
+    model_str = r"""
     group
     {
         ZERO # comment
         ONE
         TWO
     }
-    '''
+    """
 
     model = mm.model_from_str(model_str)
     assert len(model.lines) == 1
     group = model.lines[0]
     assert len(group.entries) == 3
-    assert group.entries[0].keyword == 'ZERO'
-    assert group.entries[0].modifier == ''
-    assert group.entries[1].keyword == 'ONE'
-    assert group.entries[1].modifier == ''
-    assert group.entries[2].keyword == 'TWO'
-    assert group.entries[2].modifier == ''
+    assert group.entries[0].keyword == "ZERO"
+    assert group.entries[0].modifier == ""
+    assert group.entries[1].keyword == "ONE"
+    assert group.entries[1].modifier == ""
+    assert group.entries[2].keyword == "TWO"
+    assert group.entries[2].modifier == ""
 
-    model_str = r'''
+    model_str = r"""
     group
     {  # this should work
         ZERO
         ONE
         TWO
     }
-    '''
+    """
     model = mm.model_from_str(model_str)
 
-    model_str = r'''
+    model_str = r"""
     group
     {
         ZERO
@@ -75,5 +74,5 @@ def test_comments_skipws_optimization():
         TWO
     } # this should work also
 
-    '''
+    """
     model = mm.model_from_str(model_str)
diff --git a/tests/functional/regressions/test_issue_80_object_processors.py b/tests/functional/regressions/test_issue_80_object_processors.py
index fce29c75f..4fb088381 100644
--- a/tests/functional/regressions/test_issue_80_object_processors.py
+++ b/tests/functional/regressions/test_issue_80_object_processors.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 from textx.metamodel import metamodel_from_str
 
 call_counter = 0
@@ -20,7 +19,7 @@
 ;
 """
 
-grammar2 = u"""
+grammar2 = """
 foo:
     'foo' m_formula = Formula
 ;
@@ -38,7 +37,7 @@
 ;
 """
 
-grammar3 = u"""
+grammar3 = """
 foo:
     'foo' m_formula = Formula
 ;
@@ -69,8 +68,7 @@ def parse_str(grammar, lola_str):
         "bar": default_processor,
     }
 
-    meta_model = metamodel_from_str(grammar, ignore_case=True,
-                                    auto_init_attributes=False)
+    meta_model = metamodel_from_str(grammar, ignore_case=True, auto_init_attributes=False)
     meta_model.register_obj_processors(obj_processors)
     model = meta_model.model_from_str(lola_str)
     return model
diff --git a/tests/functional/regressions/test_issue_89.py b/tests/functional/regressions/test_issue_89.py
index 3810dc4b7..79d7ba01e 100644
--- a/tests/functional/regressions/test_issue_89.py
+++ b/tests/functional/regressions/test_issue_89.py
@@ -1,20 +1,24 @@
-from __future__ import unicode_literals
 import textx
 
 
 def test_issue89_get_obj_pos_in_text():
-    mm = textx.metamodel_from_str('''
+    mm = textx.metamodel_from_str(
+        """
         Model: objs+=Obj;
         Obj: 'obj' name=ID;
-    ''')
-    m = mm.model_from_str('''obj A
+    """
+    )
+    m = mm.model_from_str(
+        """obj A
 obj B
  obj C
          obj D
-    ''')
-    assert (1, 1) == textx.get_model(m.objs[0])._tx_parser.pos_to_linecol(
-        m.objs[0]._tx_position)
+    """
+    )
+    assert textx.get_model(m.objs[0])._tx_parser.pos_to_linecol(
+        m.objs[0]._tx_position
+    ) == (1, 1)
 
-    assert (2, 1) == m._tx_parser.pos_to_linecol(m.objs[1]._tx_position)
-    assert (3, 2) == m._tx_parser.pos_to_linecol(m.objs[2]._tx_position)
-    assert (4, 10) == m._tx_parser.pos_to_linecol(m.objs[3]._tx_position)
+    assert m._tx_parser.pos_to_linecol(m.objs[1]._tx_position) == (2, 1)
+    assert m._tx_parser.pos_to_linecol(m.objs[2]._tx_position) == (3, 2)
+    assert m._tx_parser.pos_to_linecol(m.objs[3]._tx_position) == (4, 10)
diff --git a/tests/functional/regressions/test_json_issue.py b/tests/functional/regressions/test_json_issue.py
index fba1ceb60..e672ec6e5 100644
--- a/tests/functional/regressions/test_json_issue.py
+++ b/tests/functional/regressions/test_json_issue.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 from textx.metamodel import metamodel_from_str
 from textx.const import RULE_ABSTRACT, RULE_COMMON
@@ -30,8 +29,8 @@ def test_json_issue():
     ;
     """
     json_mm = metamodel_from_str(grammar)
-    assert json_mm['Object']._tx_type is RULE_COMMON
-    assert json_mm['Member']._tx_type is RULE_COMMON
-    assert json_mm['Array']._tx_type is RULE_COMMON
-    assert json_mm['File']._tx_type is RULE_ABSTRACT
-    assert json_mm['Value']._tx_type is RULE_ABSTRACT
+    assert json_mm["Object"]._tx_type is RULE_COMMON
+    assert json_mm["Member"]._tx_type is RULE_COMMON
+    assert json_mm["Array"]._tx_type is RULE_COMMON
+    assert json_mm["File"]._tx_type is RULE_ABSTRACT
+    assert json_mm["Value"]._tx_type is RULE_ABSTRACT
diff --git a/tests/functional/regressions/test_noskipws_with_int.py b/tests/functional/regressions/test_noskipws_with_int.py
index 25be70075..381b6d266 100644
--- a/tests/functional/regressions/test_noskipws_with_int.py
+++ b/tests/functional/regressions/test_noskipws_with_int.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 from textx import metamodel_from_str
 
 
@@ -6,7 +5,7 @@ def test_noskipws_modifier_with_int_rule():
     """
     Tests that noskipsws rule modifier works with INT rule.
     """
-    grammar = r'''
+    grammar = r"""
     AssessmentEnd:
         'completion_time' ':' /\s*/ completion_time=TimeHours
     ;
@@ -14,10 +13,10 @@ def test_noskipws_modifier_with_int_rule():
     TimeHours[noskipws]:
         hours=INT 'h'
     ;
-    '''
+    """
 
     meta = metamodel_from_str(grammar)
-    s = '''completion_time: 1h'''
+    s = """completion_time: 1h"""
 
     model = meta.model_from_str(s)
     assert model is not None
diff --git a/tests/functional/regressions/test_repetition_single_assignment_error.py b/tests/functional/regressions/test_repetition_single_assignment_error.py
index c7ac744e8..07e096dc0 100644
--- a/tests/functional/regressions/test_repetition_single_assignment_error.py
+++ b/tests/functional/regressions/test_repetition_single_assignment_error.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 from textx.metamodel import metamodel_from_str
 from textx.exceptions import TextXSemanticError
diff --git a/tests/functional/subcommands/example_project/pyproject.toml b/tests/functional/subcommands/example_project/pyproject.toml
new file mode 100644
index 000000000..5ee7009b9
--- /dev/null
+++ b/tests/functional/subcommands/example_project/pyproject.toml
@@ -0,0 +1,11 @@
+[project]
+name = "textX-subcommand-test"
+version = "1.0.0"
+
+dependencies = [
+    "textX"
+]
+
+[project.entry-points.textx_commands]
+testcommand = "textx_subcommand_test.cli:testcommand"
+testgroup = "textx_subcommand_test.cli:create_testgroup"
diff --git a/tests/functional/subcommands/example_project/setup.cfg b/tests/functional/subcommands/example_project/setup.cfg
deleted file mode 100644
index 2e00b21ed..000000000
--- a/tests/functional/subcommands/example_project/setup.cfg
+++ /dev/null
@@ -1,13 +0,0 @@
-[metadata]
-name = textX-subcommand-test
-version = 1.0.0
-
-[options]
-packages = find:
-install_requires =
-    textX
-
-[options.entry_points]
-textx_commands =
-    testcommand = textx_subcommand_test.cli:testcommand
-    testgroup = textx_subcommand_test.cli:create_testgroup
\ No newline at end of file
diff --git a/tests/functional/subcommands/example_project/setup.py b/tests/functional/subcommands/example_project/setup.py
deleted file mode 100644
index 43deb57b9..000000000
--- a/tests/functional/subcommands/example_project/setup.py
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-from setuptools import setup
-
-setup()
diff --git a/tests/functional/subcommands/example_project/textx_subcommand_test/cli.py b/tests/functional/subcommands/example_project/textx_subcommand_test/cli.py
index 5c50b1485..a74c1478f 100644
--- a/tests/functional/subcommands/example_project/textx_subcommand_test/cli.py
+++ b/tests/functional/subcommands/example_project/textx_subcommand_test/cli.py
@@ -3,9 +3,13 @@
 
 def testcommand(textx):
     @textx.command()
-    @click.argument('some_argument', type=click.Path())
-    @click.option('--some-option', default=False, is_flag=True,
-                  help="Testing option in custom command.")
+    @click.argument("some_argument", type=click.Path())
+    @click.option(
+        "--some-option",
+        default=False,
+        is_flag=True,
+        help="Testing option in custom command.",
+    )
     def testcommand(some_argument, some_option):
         """
         This command will be found as a sub-command of `textx` command once
@@ -16,23 +20,26 @@ def testcommand(some_argument, some_option):
 
 def create_testgroup(textx):
     @textx.group()
-    @click.option('--group-option', default=False, is_flag=True,
-                  help="Some group option.")
+    @click.option(
+        "--group-option", default=False, is_flag=True, help="Some group option."
+    )
     def testgroup(group_option):
         """Here we write group explanation."""
         pass
 
     @testgroup.command()
-    @click.argument('some_argument', type=click.Path())
-    @click.option('--some-option', default=False, is_flag=True,
-                  help="Testing option in custom command.")
+    @click.argument("some_argument", type=click.Path())
+    @click.option(
+        "--some-option",
+        default=False,
+        is_flag=True,
+        help="Testing option in custom command.",
+    )
     def groupcommand1(some_argument, some_option):
         """And here we write a doc for particular command."""
-        click.echo("GroupCommand1: argument: {}, option:{}".format(
-            some_argument, some_option))
+        click.echo(f"GroupCommand1: argument: {some_argument}, option:{some_option}")
 
     @testgroup.command()
     def groupcommand2(some_argument, some_option):
         """This is another command docs."""
-        click.echo("GroupCommand2: argument: {}, option:{}".format(
-            some_argument, some_option))
+        click.echo(f"GroupCommand2: argument: {some_argument}, option:{some_option}")
diff --git a/tests/functional/subcommands/test_subcommands.py b/tests/functional/subcommands/test_subcommands.py
index c33babae8..d6cb1a599 100644
--- a/tests/functional/subcommands/test_subcommands.py
+++ b/tests/functional/subcommands/test_subcommands.py
@@ -5,15 +5,14 @@ def test_subcommand():
     """
     Test that a command from the example project is registered.
     """
-    output = subprocess.check_output(['textx'], stderr=subprocess.STDOUT)
-    assert b'testcommand' in output
+    output = subprocess.check_output(["textx"], stderr=subprocess.STDOUT)
+    assert b"testcommand" in output
 
 
 def test_subcommand_group():
     """
     Test that a command group is registered.
     """
-    output = subprocess.check_output(['textx', 'testgroup'],
-                                     stderr=subprocess.STDOUT)
-    assert b'groupcommand1' in output
-    assert b'groupcommand2' in output
+    output = subprocess.check_output(["textx", "testgroup"], stderr=subprocess.STDOUT)
+    assert b"groupcommand1" in output
+    assert b"groupcommand2" in output
diff --git a/tests/functional/test_auto_init.py b/tests/functional/test_auto_init.py
index 529ed68ee..d2b41d62f 100644
--- a/tests/functional/test_auto_init.py
+++ b/tests/functional/test_auto_init.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
 from textx import metamodel_from_str
 
 grammar = """
@@ -28,7 +26,7 @@ def test_auto_init():
 
     mm = metamodel_from_str(grammar)
 
-    model = mm.model_from_str('42')
+    model = mm.model_from_str("42")
 
     assert model.seconds == []
 
@@ -52,7 +50,7 @@ def test_no_auto_init():
 
     mm = metamodel_from_str(grammar, auto_init_attributes=False)
 
-    model = mm.model_from_str('42')
+    model = mm.model_from_str("42")
 
     # Many multiplicity will have python array as value
     assert model.seconds == []
diff --git a/tests/functional/test_examples.py b/tests/functional/test_examples.py
index 59e543f52..bd50ec2a0 100644
--- a/tests/functional/test_examples.py
+++ b/tests/functional/test_examples.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 #######################################################################
 # Name: test_examples
 # Purpose: Test that examples run without errors.
@@ -15,15 +14,16 @@
 
 
 def test_examples():
-
-    examples_pat = os.path.join(os.path.abspath(os.path.dirname(__file__)),
-                                '../../examples/*/*.py')
+    examples_pat = os.path.join(
+        os.path.abspath(os.path.dirname(__file__)), "../../examples/*/*.py"
+    )
 
     # Filter out __init__.py and files not eligible for test run
-    examples = [f for f in
-                sorted(glob.glob(examples_pat))
-                if not any(f.endswith(a)
-                           for a in ['__init__.py', 'render_all_grammars.py'])]
+    examples = [
+        f
+        for f in sorted(glob.glob(examples_pat))
+        if not any(f.endswith(a) for a in ["__init__.py", "render_all_grammars.py"])
+    ]
 
     example_modules = []
     for e in examples:
@@ -34,11 +34,11 @@ def test_examples():
         (module_name, _) = os.path.splitext(os.path.basename(e))
         mod = importlib.import_module(module_name)
 
-        if hasattr(mod, 'main'):
+        if hasattr(mod, "main"):
             mod.main(debug=False)
             example_modules.append(mod)
 
-    print('Tested examples:')
+    print("Tested examples:")
     for e in example_modules:
         print(e.__file__)
     assert len(example_modules) == 12
diff --git a/tests/functional/test_import/test_import.py b/tests/functional/test_import/test_import.py
index bcbc97bc1..15d61c5ab 100644
--- a/tests/functional/test_import/test_import.py
+++ b/tests/functional/test_import/test_import.py
@@ -1,7 +1,7 @@
-from __future__ import unicode_literals
-import pytest
 import os
 
+import pytest
+
 from textx import metamodel_from_file, metamodel_from_str
 from textx.export import metamodel_export, model_export
 
@@ -12,9 +12,8 @@ def test_import():
     """
 
     current_dir = os.path.dirname(__file__)
-    mm = metamodel_from_file(os.path.join(current_dir,
-                             'relativeimport', 'first.tx'))
-    metamodel_export(mm, 'import_test_mm.dot')
+    mm = metamodel_from_file(os.path.join(current_dir, "relativeimport", "first.tx"))
+    metamodel_export(mm, "import_test_mm.dot")
 
     model = """
     first
@@ -24,7 +23,7 @@ def test_import():
     """
 
     model = mm.model_from_str(model)
-    model_export(model, 'import_test_model.dot')
+    model_export(model, "import_test_model.dot")
 
 
 def test_multiple_imports():
@@ -34,12 +33,13 @@ def test_multiple_imports():
     """
 
     current_dir = os.path.dirname(__file__)
-    mm = metamodel_from_file(os.path.join(current_dir,
-                             'multiple', 'first.tx'))
+    mm = metamodel_from_file(os.path.join(current_dir, "multiple", "first.tx"))
 
-    assert mm['First']._tx_attrs['seconds'].cls._tx_attrs['thirds'].cls \
-        is mm['relative.third.Third']
-    metamodel_export(mm, 'multipleimport_test_mm.dot')
+    assert (
+        mm["First"]._tx_attrs["seconds"].cls._tx_attrs["thirds"].cls
+        is mm["relative.third.Third"]
+    )
+    metamodel_export(mm, "multipleimport_test_mm.dot")
 
     model = """
         first 1 2
@@ -47,7 +47,7 @@ def test_multiple_imports():
     """
 
     model = mm.model_from_str(model)
-    model_export(model, 'multipleimport_test_model.dot')
+    model_export(model, "multipleimport_test_model.dot")
 
 
 def test_no_import_for_string():
diff --git a/tests/functional/test_language_reference/test_language_reference.py b/tests/functional/test_language_reference/test_language_reference.py
index 279506a6e..55445a950 100644
--- a/tests/functional/test_language_reference/test_language_reference.py
+++ b/tests/functional/test_language_reference/test_language_reference.py
@@ -1,29 +1,29 @@
 """
 Testing `reference` grammar keyword.
 """
-from __future__ import unicode_literals
 import os
-from textx import (metamodel_from_str, language, metamodel_for_language,
-                   register_language)
+
 import textx.scoping.providers as scoping_providers
+from textx import language, metamodel_for_language, metamodel_from_str, register_language
 
 
 def test_language_reference_keyword():
-
-    @language('first-test-lang', '*.ftest')
+    @language("first-test-lang", "*.ftest")
     def first_language():
         return metamodel_from_str(
-            r'''
+            r"""
             Model: firsts*=First;
             First: name=ID num=INT;
-            ''')
+            """
+        )
+
     register_language(first_language)
 
-    @language('second-test-lang', '*.stest')
+    @language("second-test-lang", "*.stest")
     def second_language():
         # We can reference here fist-test-lang since it is registered above
         mm = metamodel_from_str(
-            r'''
+            r"""
             reference first-test-lang as f
 
             Model:
@@ -31,18 +31,20 @@ def second_language():
                 refs+=Reference;
             Reference: 'ref' ref=[f.First];
             Include: 'include' importURI=STRING;
-            ''', global_repository=True)
-        mm.register_scope_providers(
-            {"*.*": scoping_providers.FQNImportURI()})
+            """,
+            global_repository=True,
+        )
+        mm.register_scope_providers({"*.*": scoping_providers.FQNImportURI()})
         return mm
+
     register_language(second_language)
 
-    mm = metamodel_for_language('second-test-lang')
+    mm = metamodel_for_language("second-test-lang")
 
     current_dir = os.path.dirname(__file__)
-    p = os.path.join(current_dir, 'model.stest')
+    p = os.path.join(current_dir, "model.stest")
     model = mm.model_from_file(p)
 
     assert len(model.refs) == 2
-    assert model.refs[0].ref.name == 'first1'
+    assert model.refs[0].ref.name == "first1"
     assert model.refs[0].ref.num == 42
diff --git a/tests/functional/test_metaclass_reference.py b/tests/functional/test_metaclass_reference.py
index ab92947e4..880346c05 100644
--- a/tests/functional/test_metaclass_reference.py
+++ b/tests/functional/test_metaclass_reference.py
@@ -1,7 +1,8 @@
-from __future__ import unicode_literals
-import pytest
 import os
-from textx import metamodel_from_str, metamodel_from_file
+
+import pytest
+
+from textx import metamodel_from_file, metamodel_from_str
 
 grammar = """
 First:
@@ -17,8 +18,8 @@
 
 def test_metaclass_ref():
     metamodel = metamodel_from_str(grammar)
-    First = metamodel['First']
-    Second = metamodel['Second']
+    First = metamodel["First"]
+    Second = metamodel["Second"]
 
     model = metamodel.model_from_str('first 45 "test" 12')
 
@@ -30,23 +31,25 @@ def test_metaclass_user_class():
     """
     User supplied meta class.
     """
-    class First(object):
+
+    class First:
         def __init__(self, seconds):
             self.seconds = seconds
 
     metamodel = metamodel_from_str(grammar, classes=[First])
 
-    model = metamodel.model_from_str('first 45 12')
+    model = metamodel.model_from_str("first 45 12")
     assert type(model) is First
 
 
 @pytest.mark.parametrize("filename", ["first.tx", "first_new.tx"])
 def test_metaclass_relative_paths(filename):
     current_dir = os.path.dirname(__file__)
-    mm = metamodel_from_file(os.path.join(current_dir, 'test_import',
-                                          'importoverride', filename))
-    Third = mm['Third']
-    ThirdMasked = mm['relative.third.Third']
+    mm = metamodel_from_file(
+        os.path.join(current_dir, "test_import", "importoverride", filename)
+    )
+    Third = mm["Third"]
+    ThirdMasked = mm["relative.third.Third"]
     assert Third is not ThirdMasked
 
     model = mm.model_from_str('first 12 45 third "abc" "xyz"')
@@ -62,13 +65,13 @@ def test_diamond_import():
     """
 
     current_dir = os.path.dirname(__file__)
-    mm = metamodel_from_file(os.path.join(current_dir, 'test_import',
-                                          'importoverride',
-                                          'first_diamond.tx'))
-    First = mm['First']
-    MyDiamondRule = mm['diamond.last.MyDiamondRule']
+    mm = metamodel_from_file(
+        os.path.join(current_dir, "test_import", "importoverride", "first_diamond.tx")
+    )
+    First = mm["First"]
+    MyDiamondRule = mm["diamond.last.MyDiamondRule"]
 
-    model = mm.model_from_str('second 12 45 third 4 5')
+    model = mm.model_from_str("second 12 45 third 4 5")
 
     assert type(model) is First
     assert all(type(x.diamond) is MyDiamondRule for x in model.seconds)
diff --git a/tests/functional/test_metamodel/test_metamodel.py b/tests/functional/test_metamodel/test_metamodel.py
index a7642ebc5..018951b2b 100644
--- a/tests/functional/test_metamodel/test_metamodel.py
+++ b/tests/functional/test_metamodel/test_metamodel.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 import os
 from textx import metamodel_from_str, metamodel_from_file
@@ -21,7 +20,7 @@ def test_textx_metaclass_repr():
     """
 
     metamodel = metamodel_from_str(grammar)
-    assert '<textx:First class at' in repr(metamodel['First'])
+    assert "<textx:First class at" in repr(metamodel["First"])
 
 
 def test_textx_metaclass_instance_repr():
@@ -30,8 +29,8 @@ def test_textx_metaclass_instance_repr():
     """
 
     metamodel = metamodel_from_str(grammar)
-    model = metamodel.model_from_str('first 42')
-    assert '<textx:First instance at' in repr(model)
+    model = metamodel.model_from_str("first 42")
+    assert "<textx:First instance at" in repr(model)
 
 
 def test_textx_imported_metaclass_repr():
@@ -40,11 +39,10 @@ def test_textx_imported_metaclass_repr():
     """
 
     current_dir = os.path.dirname(__file__)
-    mm = metamodel_from_file(os.path.join(current_dir, 'import',
-                                          'first_diamond.tx'))
-    MyDiamondRule = mm['diamond.last.MyDiamondRule']
+    mm = metamodel_from_file(os.path.join(current_dir, "import", "first_diamond.tx"))
+    MyDiamondRule = mm["diamond.last.MyDiamondRule"]
 
-    assert '<textx:diamond.last.MyDiamondRule class at' in repr(MyDiamondRule)
+    assert "<textx:diamond.last.MyDiamondRule class at" in repr(MyDiamondRule)
 
 
 def test_textx_imported_metaclass_instance_repr():
@@ -53,20 +51,17 @@ def test_textx_imported_metaclass_instance_repr():
     """
 
     current_dir = os.path.dirname(__file__)
-    mm = metamodel_from_file(os.path.join(current_dir, 'import',
-                                          'first_diamond.tx'))
-    model = mm.model_from_str('second 42 11 third 42')
+    mm = metamodel_from_file(os.path.join(current_dir, "import", "first_diamond.tx"))
+    model = mm.model_from_str("second 42 11 third 42")
 
-    assert '<textx:diamond.last.MyDiamondRule instance at' \
-        in repr(model.seconds[0].diamond)
+    assert "<textx:diamond.last.MyDiamondRule instance at" in repr(
+        model.seconds[0].diamond
+    )
 
 
 def test_textx_imported_metaclass_repr_same_level_import():
     current_dir = os.path.dirname(__file__)
-    mm = metamodel_from_file(os.path.join(current_dir, 'import',
-                                          'repr',
-                                          'first_repr.tx'))
-    model = mm.model_from_str('second 42')
+    mm = metamodel_from_file(os.path.join(current_dir, "import", "repr", "first_repr.tx"))
+    model = mm.model_from_str("second 42")
 
-    assert '<textx:third_repr.Third instance at' \
-        in repr(model.seconds[0].thirds[0])
+    assert "<textx:third_repr.Third instance at" in repr(model.seconds[0].thirds[0])
diff --git a/tests/functional/test_metamodel/test_model_params.py b/tests/functional/test_metamodel/test_model_params.py
index 38dd21691..9a622fcf3 100644
--- a/tests/functional/test_metamodel/test_model_params.py
+++ b/tests/functional/test_metamodel/test_model_params.py
@@ -1,15 +1,18 @@
-from __future__ import unicode_literals
+import os.path
 
 from click.testing import CliRunner
-import os.path
 from pytest import raises
 
-from textx import metamodel_from_str
+from textx import (
+    generator,
+    language,
+    metamodel_from_str,
+    register_generator,
+    register_language,
+)
 from textx.cli import textx
 from textx.exceptions import TextXError
 from textx.generators import gen_file, get_output_filename
-from textx import language, generator, register_language, register_generator
-
 
 grammar = r"""
 Model: 'MyModel' name=ID;
@@ -22,62 +25,54 @@
 
 def test_model_params():
     mm = metamodel_from_str(grammar)
-    mm.model_param_defs.add(
-        "parameter1", "an example param (1)"
-    )
-    mm.model_param_defs.add(
-        "parameter2", "an example param (2)"
-    )
+    mm.model_param_defs.add("parameter1", "an example param (1)")
+    mm.model_param_defs.add("parameter2", "an example param (2)")
 
-    m = mm.model_from_str(text, parameter1='P1', parameter2='P2')
-    assert m.name == 'test1'
-    assert hasattr(m, '_tx_model_params')
+    m = mm.model_from_str(text, parameter1="P1", parameter2="P2")
+    assert m.name == "test1"
+    assert hasattr(m, "_tx_model_params")
     assert len(m._tx_model_params) == 2
     assert len(m._tx_model_params.used_keys) == 0
 
     assert not m._tx_model_params.all_used
 
-    assert m._tx_model_params['parameter1'] == 'P1'
+    assert m._tx_model_params["parameter1"] == "P1"
     assert len(m._tx_model_params.used_keys) == 1
-    assert 'parameter1' in m._tx_model_params.used_keys
-    assert 'parameter2' not in m._tx_model_params.used_keys
+    assert "parameter1" in m._tx_model_params.used_keys
+    assert "parameter2" not in m._tx_model_params.used_keys
 
     assert not m._tx_model_params.all_used
 
-    assert m._tx_model_params['parameter2'] == 'P2'
+    assert m._tx_model_params["parameter2"] == "P2"
     assert len(m._tx_model_params.used_keys) == 2
-    assert 'parameter1' in m._tx_model_params.used_keys
-    assert 'parameter2' in m._tx_model_params.used_keys
+    assert "parameter1" in m._tx_model_params.used_keys
+    assert "parameter2" in m._tx_model_params.used_keys
 
     assert m._tx_model_params.all_used
 
-    assert m._tx_model_params.get(
-        'missing_params', default='default value') == 'default value'
-    assert m._tx_model_params.get(
-        'parameter1', default='default value') == 'P1'
+    assert (
+        m._tx_model_params.get("missing_params", default="default value")
+        == "default value"
+    )
+    assert m._tx_model_params.get("parameter1", default="default value") == "P1"
 
     with raises(TextXError, match=".*unknown parameter myerror2.*"):
-        mm.model_from_str(text, parameter1='P1', myerror2='P2')
+        mm.model_from_str(text, parameter1="P1", myerror2="P2")
 
     assert len(mm.model_param_defs) >= 2
-    assert 'parameter1' in mm.model_param_defs
-    assert 'parameter1' in mm.model_param_defs
-    assert mm.model_param_defs[
-        'parameter1'].description == "an example param (1)"
+    assert "parameter1" in mm.model_param_defs
+    assert "parameter1" in mm.model_param_defs
+    assert mm.model_param_defs["parameter1"].description == "an example param (1)"
 
 
 def test_model_params_empty():
     mm = metamodel_from_str(grammar)
-    mm.model_param_defs.add(
-        "parameter1", "an example param (1)"
-    )
-    mm.model_param_defs.add(
-        "parameter2", "an example param (2)"
-    )
+    mm.model_param_defs.add("parameter1", "an example param (1)")
+    mm.model_param_defs.add("parameter2", "an example param (2)")
 
     m = mm.model_from_str(text)
-    assert m.name == 'test1'
-    assert hasattr(m, '_tx_model_params')
+    assert m.name == "test1"
+    assert hasattr(m, "_tx_model_params")
     assert len(m._tx_model_params) == 0
 
     assert m._tx_model_params.all_used
@@ -85,20 +80,17 @@ def test_model_params_empty():
 
 def test_model_params_file_based():
     mm = metamodel_from_str(grammar)
-    mm.model_param_defs.add(
-        "parameter1", "an example param (1)"
-    )
-    mm.model_param_defs.add(
-        "parameter2", "an example param (2)"
-    )
+    mm.model_param_defs.add("parameter1", "an example param (1)")
+    mm.model_param_defs.add("parameter2", "an example param (2)")
 
     current_dir = os.path.dirname(__file__)
     m = mm.model_from_file(
-        os.path.join(current_dir, 'test_model_params',
-                     'model.txt'),
-        parameter1='P1', parameter2='P2')
-    assert m.name == 'file_based'
-    assert hasattr(m, '_tx_model_params')
+        os.path.join(current_dir, "test_model_params", "model.txt"),
+        parameter1="P1",
+        parameter2="P2",
+    )
+    assert m.name == "file_based"
+    assert hasattr(m, "_tx_model_params")
     assert len(m._tx_model_params) == 2
 
 
@@ -108,38 +100,44 @@ def test_model_params_generate_cli():
     """
 
     # register test language
-    @language('testlang', '*.mpt')
+    @language("testlang", "*.mpt")
     def model_param_test():
-
         def processor(model, metamodel):
             # Just to be sure that processor sees the model parameters
             model.model_params = model._tx_model_params
 
         mm = metamodel_from_str(grammar)
-        mm.model_param_defs.add('meaning_of_life', 'The Meaning of Life')
+        mm.model_param_defs.add("meaning_of_life", "The Meaning of Life")
         mm.register_model_processor(processor)
         return mm
+
     register_language(model_param_test)
 
     # register language generator
-    @generator('testlang', 'testtarget')
-    def mytarget_generator(metamodel, model, output_path, overwrite,
-                           debug=False, **custom_args):
-
+    @generator("testlang", "testtarget")
+    def mytarget_generator(
+        metamodel, model, output_path, overwrite, debug=False, **custom_args
+    ):
         # Dump custom args for testing
-        txt = '\n'.join(["{}={}".format(arg_name, arg_value)
-                         for arg_name, arg_value in custom_args.items()])
+        txt = "\n".join(
+            [f"{arg_name}={arg_value}" for arg_name, arg_value in custom_args.items()]
+        )
 
         # Dump model params processed by model processor for testing
-        txt += '\nModel params:'
-        txt += '\n'.join(["{}={}".format(param_name, param_value)
-                          for param_name, param_value in model.model_params.items()])
+        txt += "\nModel params:"
+        txt += "\n".join(
+            [
+                f"{param_name}={param_value}"
+                for param_name, param_value in model.model_params.items()
+            ]
+        )
 
-        output_file = get_output_filename(model._tx_filename, None, 'testtarget')
+        output_file = get_output_filename(model._tx_filename, None, "testtarget")
 
         def gen_callback():
-            with open(output_file, 'w') as f:
+            with open(output_file, "w") as f:
                 f.write(txt)
+
         gen_file(model._tx_filename, output_file, gen_callback, overwrite)
 
     register_generator(mytarget_generator)
@@ -147,19 +145,29 @@ def gen_callback():
     # Run generator from CLI
     this_folder = os.path.abspath(os.path.dirname(__file__))
     runner = CliRunner()
-    model_file = os.path.join(this_folder, 'model_param_generate_test.mpt')
-    result = runner.invoke(textx, ['generate',
-                                   '--language', 'testlang',
-                                   '--target', 'testtarget',
-                                   '--overwrite', model_file,
-                                   '--meaning_of_life', '42',
-                                   '--someparam', 'somevalue'])
+    model_file = os.path.join(this_folder, "model_param_generate_test.mpt")
+    result = runner.invoke(
+        textx,
+        [
+            "generate",
+            "--language",
+            "testlang",
+            "--target",
+            "testtarget",
+            "--overwrite",
+            model_file,
+            "--meaning_of_life",
+            "42",
+            "--someparam",
+            "somevalue",
+        ],
+    )
 
     assert result.exit_code == 0
 
-    output_file = os.path.join(this_folder, 'model_param_generate_test.testtarget')
-    with open(output_file, 'r') as f:
+    output_file = os.path.join(this_folder, "model_param_generate_test.testtarget")
+    with open(output_file) as f:
         content = f.read()
 
-    assert 'someparam=somevalue' in content
-    assert 'Model params:meaning_of_life=42' in content
+    assert "someparam=somevalue" in content
+    assert "Model params:meaning_of_life=42" in content
diff --git a/tests/functional/test_metamodel/test_multi_metamodel_refs.py b/tests/functional/test_metamodel/test_multi_metamodel_refs.py
index 6a030ab6b..ff716b601 100644
--- a/tests/functional/test_metamodel/test_multi_metamodel_refs.py
+++ b/tests/functional/test_metamodel/test_multi_metamodel_refs.py
@@ -1,11 +1,14 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 import os
 import os.path
 from pytest import raises
-from textx import (get_location, metamodel_from_str,
-                   metamodel_for_language,
-                   register_language, clear_language_registrations)
+from textx import (
+    get_location,
+    metamodel_from_str,
+    metamodel_for_language,
+    register_language,
+    clear_language_registrations,
+)
 import textx.scoping.providers as scoping_providers
 import textx.scoping as scoping
 import textx.exceptions
@@ -29,15 +32,14 @@
 
 
 def register_languages():
-
     clear_language_registrations()
 
     global_repo = scoping.GlobalModelRepository()
     global_repo_provider = scoping_providers.PlainNameGlobalRepo()
 
-    class A(object):
+    class A:
         def __init__(self, **kwargs):
-            super(A, self).__init__()
+            super().__init__()
             for k, v in kwargs.items():
                 self.__dict__[k] = v
 
@@ -45,8 +47,7 @@ def __setattr__(self, name, value):
             raise Exception("test: this is not allowed.")
 
     def get_A_mm():
-        mm_A = metamodel_from_str(grammarA, global_repository=global_repo,
-                                  classes=[A])
+        mm_A = metamodel_from_str(grammarA, global_repository=global_repo, classes=[A])
         mm_A.register_scope_providers({"*.*": global_repo_provider})
         return mm_A
 
@@ -56,48 +57,44 @@ def get_B_mm():
         return mm_B
 
     def get_BwithImport_mm():
-        mm_B = metamodel_from_str(grammarBWithImport,
-                                  global_repository=global_repo)
+        mm_B = metamodel_from_str(grammarBWithImport, global_repository=global_repo)
 
         # define a default scope provider supporting the importURI feature
-        mm_B.register_scope_providers(
-            {"*.*": scoping_providers.FQNImportURI()})
+        mm_B.register_scope_providers({"*.*": scoping_providers.FQNImportURI()})
         return mm_B
 
-    register_language('A',
-                      pattern="*.a",
-                      metamodel=get_A_mm)
-    register_language('B',
-                      pattern="*.b",
-                      metamodel=get_B_mm)
+    register_language("A", pattern="*.a", metamodel=get_A_mm)
+    register_language("B", pattern="*.b", metamodel=get_B_mm)
 
-    register_language('BwithImport',
-                      pattern="*.b",
-                      metamodel=get_BwithImport_mm)
+    register_language("BwithImport", pattern="*.b", metamodel=get_BwithImport_mm)
 
     return global_repo_provider
 
 
 def test_multi_metamodel_references1():
-
     global_repo_provider = register_languages()
 
-    mm_A = metamodel_for_language('A')
-    mA = mm_A.model_from_str('''
+    mm_A = metamodel_for_language("A")
+    mA = mm_A.model_from_str(
+        """
     A a1 A a2 A a3
-    ''')
+    """
+    )
     global_repo_provider.add_model(mA)
 
-    mm_B = metamodel_for_language('B')
-    mm_B.model_from_str('''
+    mm_B = metamodel_for_language("B")
+    mm_B.model_from_str(
+        """
     B b1 -> a1 B b2 -> a2 B b3 -> a3
-    ''')
+    """
+    )
 
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*UNKNOWN.*'):
-        mm_B.model_from_str('''
+    with raises(textx.exceptions.TextXSemanticError, match=r".*UNKNOWN.*"):
+        mm_B.model_from_str(
+            """
         B b1 -> a1 B b2 -> a2 B b3 -> UNKNOWN
-        ''')
+        """
+        )
 
 
 def test_multi_metamodel_references2():
@@ -107,20 +104,25 @@ def test_multi_metamodel_references2():
     global_repo_provider = scoping_providers.PlainNameGlobalRepo()
     mm_B.register_scope_providers({"*.*": global_repo_provider})
 
-    mA = mm_A.model_from_str('''
+    mA = mm_A.model_from_str(
+        """
     A a1 A a2 A a3
-    ''')
+    """
+    )
     global_repo_provider.add_model(mA)
 
-    mm_B.model_from_str('''
+    mm_B.model_from_str(
+        """
     B b1 -> a1 B b2 -> a2 B b3 -> a3
-    ''')
+    """
+    )
 
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*UNKNOWN.*'):
-        mm_B.model_from_str('''
+    with raises(textx.exceptions.TextXSemanticError, match=r".*UNKNOWN.*"):
+        mm_B.model_from_str(
+            """
         B b1 -> a1 B b2 -> a2 B b3 -> UNKNOWN
-        ''')
+        """
+        )
 
 
 def test_multi_metamodel_references_with_importURI():
@@ -145,29 +147,30 @@ def test_multi_metamodel_references_with_importURI():
 
     # Create two meta models with the global repo.
     # The second meta model allows referencing the first one.
-    mm_A = metamodel_for_language('A')
-    mm_B = metamodel_for_language('BwithImport')
+    mm_A = metamodel_for_language("A")
+    mm_B = metamodel_for_language("BwithImport")
 
-    modelA = mm_A.model_from_str('''
+    modelA = mm_A.model_from_str(
+        """
     A a1 A a2 A a3
-    ''')
+    """
+    )
 
-    with raises(Exception,
-                match=r'.*test: this is not allowed.*'):
+    with raises(Exception, match=r".*test: this is not allowed.*"):
         modelA.a[0].x = 1
 
     # load a model from B which includes a model from A.
     current_dir = os.path.dirname(__file__)
     modelB = mm_B.model_from_file(
-        os.path.join(current_dir, 'multi_metamodel', 'refs', 'b.b'))
+        os.path.join(current_dir, "multi_metamodel", "refs", "b.b")
+    )
 
     # check that the classes from the correct meta model are used
     # (and that the model was loaded).
     assert modelB.b[0].__class__ == mm_B[modelB.b[0].__class__.__name__]
     assert modelB.b[0].a.__class__ == mm_A[modelB.b[0].a.__class__.__name__]
 
-    with raises(Exception,
-                match=r'.*test: this is not allowed.*'):
+    with raises(Exception, match=r".*test: this is not allowed.*"):
         modelB.b[0].a.x = 1
 
 
@@ -175,14 +178,14 @@ def test_multi_metamodel_references_with_importURI():
 
 
 class LibTypes:
-    """ Library for Typedefs:
-            type int
-            type string
+    """Library for Typedefs:
+    type int
+    type string
     """
 
     @staticmethod
     def get_metamodel():
-        return metamodel_for_language('types')
+        return metamodel_for_language("types")
 
     @staticmethod
     def library_init(repo_selector):
@@ -191,44 +194,41 @@ def library_init(repo_selector):
         elif repo_selector == "global repo":
             global_repo = True
         else:
-            raise Exception("unexpected parameter 'repo_selector={}'"
-                            .format(repo_selector))
+            raise Exception(f"unexpected parameter 'repo_selector={repo_selector}'")
 
         def get_metamodel():
             mm = metamodel_from_str(
-                r'''
+                r"""
                     Model: types+=Type;
                     Type: 'type' name=ID;
                     Comment: /\/\/.*$/;
-                ''',
-                global_repository=global_repo)
+                """,
+                global_repository=global_repo,
+            )
 
             def check_type(t):
                 if t.name[0].isupper():
                     raise textx.exceptions.TextXSyntaxError(
-                        "types must be lowercase",
-                        **get_location(t)
+                        "types must be lowercase", **get_location(t)
                     )
-            mm.register_obj_processors({
-                'Type': check_type
-            })
+
+            mm.register_obj_processors({"Type": check_type})
 
             return mm
 
-        register_language('types', pattern='*.type',
-                          metamodel=get_metamodel)
+        register_language("types", pattern="*.type", metamodel=get_metamodel)
 
 
 class LibData:
-    """ Library for Datadefs:
-            data Point { x: int y: int}
-            data City { name: string }
-            data Population { count: int}
+    """Library for Datadefs:
+    data Point { x: int y: int}
+    data City { name: string }
+    data Population { count: int}
     """
 
     @staticmethod
     def get_metamodel():
-        return metamodel_for_language('data')
+        return metamodel_for_language("data")
 
     @staticmethod
     def library_init(repo_selector):
@@ -238,12 +238,11 @@ def library_init(repo_selector):
             # get the global repo from the inherited meta model:
             global_repo = LibTypes.get_metamodel()._tx_model_repository
         else:
-            raise Exception("unexpected parameter 'repo_selector={}'"
-                            .format(repo_selector))
+            raise Exception(f"unexpected parameter 'repo_selector={repo_selector}'")
 
         def get_metamodel():
             mm = metamodel_from_str(
-                r'''
+                r"""
                     reference types as t
                     Model: includes*=Include data+=Data;
                     Data: 'data' name=ID '{'
@@ -252,29 +251,27 @@ def get_metamodel():
                     Attribute: name=ID ':' type=[t.Type];
                     Include: '#include' importURI=STRING;
                     Comment: /\/\/.*$/;
-                ''',
-                global_repository=global_repo)
+                """,
+                global_repository=global_repo,
+            )
 
-            mm.register_scope_providers(
-                {"*.*": scoping_providers.FQNImportURI()})
+            mm.register_scope_providers({"*.*": scoping_providers.FQNImportURI()})
 
             return mm
 
-        register_language('data',
-                          pattern='*.data',
-                          metamodel=get_metamodel)
+        register_language("data", pattern="*.data", metamodel=get_metamodel)
 
 
 class LibFlow:
-    """ Library for DataFlows
-            algo A1 : Point -> City
-            algo A2 : City -> Population
-            connect A1 -> A2
+    """Library for DataFlows
+    algo A1 : Point -> City
+    algo A2 : City -> Population
+    connect A1 -> A2
     """
 
     @staticmethod
     def get_metamodel():
-        return metamodel_for_language('flow')
+        return metamodel_for_language("flow")
 
     @staticmethod
     def library_init(repo_selector):
@@ -284,44 +281,37 @@ def library_init(repo_selector):
             # get the global repo from the inherited meta model:
             global_repo = LibData.get_metamodel()._tx_model_repository
         else:
-            raise Exception("unexpected parameter 'repo_selector={}'"
-                            .format(repo_selector))
+            raise Exception(f"unexpected parameter 'repo_selector={repo_selector}'")
 
         def get_metamodel():
-
             mm = metamodel_from_str(
-                r'''
+                r"""
                     reference data as d
                     Model: includes*=Include algos+=Algo flows+=Flow;
                     Algo: 'algo' name=ID ':' inp=[d.Data] '->' outp=[d.Data];
                     Flow: 'connect' algo1=[Algo] '->' algo2=[Algo] ;
                     Include: '#include' importURI=STRING;
                     Comment: /\/\/.*$/;
-                ''',
-                global_repository=global_repo)
+                """,
+                global_repository=global_repo,
+            )
 
-            mm.register_scope_providers(
-                {"*.*": scoping_providers.FQNImportURI()})
+            mm.register_scope_providers({"*.*": scoping_providers.FQNImportURI()})
 
             def check_flow(f):
                 if f.algo1.outp != f.algo2.inp:
                     raise textx.exceptions.TextXSemanticError(
-                        "algo data types must match",
-                        **get_location(f)
+                        "algo data types must match", **get_location(f)
                     )
-            mm.register_obj_processors({
-                'Flow': check_flow
-            })
+
+            mm.register_obj_processors({"Flow": check_flow})
 
             return mm
 
-        register_language('flow',
-                          pattern='*.flow',
-                          metamodel=get_metamodel)
+        register_language("flow", pattern="*.flow", metamodel=get_metamodel)
 
 
 def test_multi_metamodel_types_data_flow1():
-
     # this stuff normally happens in the python module directly of the
     # third party lib
     selector = "no global scope"
@@ -332,37 +322,37 @@ def test_multi_metamodel_types_data_flow1():
 
     current_dir = os.path.dirname(__file__)
     model1 = LibFlow.get_metamodel().model_from_file(
-        os.path.join(current_dir, 'multi_metamodel', 'types_data_flow',
-                     'data_flow.flow')
+        os.path.join(current_dir, "multi_metamodel", "types_data_flow", "data_flow.flow")
     )
 
     # althought, types.type is included 2x, it is only present 1x
     # (scope providers share a common repo within on model and all
     #  loaded models in that model)
-    assert 3 == len(model1._tx_model_repository.all_models)
+    assert len(model1._tx_model_repository.all_models) == 3
 
     # load the type model also used by model1
     model2 = LibData.get_metamodel().model_from_file(
-        os.path.join(current_dir, 'multi_metamodel', 'types_data_flow',
-                     'data_structures.data')
+        os.path.join(
+            current_dir, "multi_metamodel", "types_data_flow", "data_structures.data"
+        )
     )
 
     # load the type model also used by model1 and model2
     model3 = LibTypes.get_metamodel().model_from_file(
-        os.path.join(current_dir, 'multi_metamodel', 'types_data_flow',
-                     'types.type')
+        os.path.join(current_dir, "multi_metamodel", "types_data_flow", "types.type")
     )
 
     # the types (reloaded by the second model)
     # are not shared with the first model
     # --> no global repo
-    assert model1.algos[0].inp.attributes[0].type \
+    assert (
+        model1.algos[0].inp.attributes[0].type
         not in model2.includes[0]._tx_loaded_models[0].types
+    )
     assert model1.algos[0].inp.attributes[0].type not in model3.types
 
 
 def test_multi_metamodel_types_data_flow2():
-
     # this stuff normally happens in the python module directly of the
     # third party lib
     selector = "global repo"
@@ -373,34 +363,34 @@ def test_multi_metamodel_types_data_flow2():
 
     current_dir = os.path.dirname(__file__)
     model1 = LibFlow.get_metamodel().model_from_file(
-        os.path.join(current_dir, 'multi_metamodel', 'types_data_flow',
-                     'data_flow.flow')
+        os.path.join(current_dir, "multi_metamodel", "types_data_flow", "data_flow.flow")
     )
     # althought, types.type is included 2x, it is only present 1x
-    assert 3 == len(model1._tx_model_repository.all_models)
+    assert len(model1._tx_model_repository.all_models) == 3
 
     # load the type model also used by model1
     model2 = LibData.get_metamodel().model_from_file(
-        os.path.join(current_dir, 'multi_metamodel', 'types_data_flow',
-                     'data_structures.data')
+        os.path.join(
+            current_dir, "multi_metamodel", "types_data_flow", "data_structures.data"
+        )
     )
 
     # load the type model also used by model1 and model2
     model3 = LibTypes.get_metamodel().model_from_file(
-        os.path.join(current_dir, 'multi_metamodel', 'types_data_flow',
-                     'types.type')
+        os.path.join(current_dir, "multi_metamodel", "types_data_flow", "types.type")
     )
 
     # the types (reloaded by the second model)
     # are shared with the first model
     # --> global repo
-    assert model1.algos[0].inp.attributes[0].type \
+    assert (
+        model1.algos[0].inp.attributes[0].type
         in model2.includes[0]._tx_loaded_models[0].types
+    )
     assert model1.algos[0].inp.attributes[0].type in model3.types
 
 
 def test_multi_metamodel_types_data_flow_validation_error_in_types():
-
     selector = "no global scope"
     clear_language_registrations()
     LibTypes.library_init(selector)
@@ -409,16 +399,18 @@ def test_multi_metamodel_types_data_flow_validation_error_in_types():
 
     current_dir = os.path.dirname(__file__)
 
-    with raises(textx.exceptions.TextXSyntaxError,
-                match=r'.*lowercase.*'):
+    with raises(textx.exceptions.TextXSyntaxError, match=r".*lowercase.*"):
         LibFlow.get_metamodel().model_from_file(
-            os.path.join(current_dir, 'multi_metamodel', 'types_data_flow',
-                         'data_flow_including_error.flow')
+            os.path.join(
+                current_dir,
+                "multi_metamodel",
+                "types_data_flow",
+                "data_flow_including_error.flow",
+            )
         )
 
 
 def test_multi_metamodel_types_data_flow_validation_error_in_data_flow():
-
     selector = "no global scope"
     clear_language_registrations()
     LibTypes.library_init(selector)
@@ -427,9 +419,12 @@ def test_multi_metamodel_types_data_flow_validation_error_in_data_flow():
 
     current_dir = os.path.dirname(__file__)
 
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*data types must match.*'):
+    with raises(textx.exceptions.TextXSemanticError, match=r".*data types must match.*"):
         LibFlow.get_metamodel().model_from_file(
-            os.path.join(current_dir, 'multi_metamodel', 'types_data_flow',
-                         'data_flow_with_error.flow')
+            os.path.join(
+                current_dir,
+                "multi_metamodel",
+                "types_data_flow",
+                "data_flow_with_error.flow",
+            )
         )
diff --git a/tests/functional/test_metamodel_params.py b/tests/functional/test_metamodel_params.py
index 8489398b0..14fbab31c 100644
--- a/tests/functional/test_metamodel_params.py
+++ b/tests/functional/test_metamodel_params.py
@@ -1,5 +1,5 @@
-from __future__ import unicode_literals
 import pytest
+
 from textx import metamodel_from_str
 from textx.exceptions import TextXSyntaxError
 
@@ -12,10 +12,10 @@ def test_ignore_case():
 
     # By default case is not ignored.
     with pytest.raises(TextXSyntaxError):
-        meta.model_from_str('Start first First Second')
+        meta.model_from_str("Start first First Second")
 
     meta = metamodel_from_str(langdef, ignore_case=True)
-    meta.model_from_str('Start first First Second')
+    meta.model_from_str("Start first First Second")
 
 
 def test_skipws():
@@ -25,13 +25,13 @@ def test_skipws():
     meta = metamodel_from_str(langdef)
 
     # By default ws are skipped.
-    meta.model_from_str('start first first second')
+    meta.model_from_str("start first first second")
 
     meta = metamodel_from_str(langdef, skipws=False)
     with pytest.raises(TextXSyntaxError):
-        meta.model_from_str('start first first second')
+        meta.model_from_str("start first first second")
 
-    meta.model_from_str('startfirstfirstsecond')
+    meta.model_from_str("startfirstfirstsecond")
 
 
 def test_ws():
@@ -41,15 +41,19 @@ def test_ws():
     meta = metamodel_from_str(langdef)
 
     # Default ws are space, tab and newline
-    meta.model_from_str("""start  first 
-            first second""")  # noqa
+    meta.model_from_str(
+        """start  first 
+            first second"""
+    )  # noqa
 
-    meta = metamodel_from_str(langdef, ws=' ')
+    meta = metamodel_from_str(langdef, ws=" ")
     with pytest.raises(TextXSyntaxError):
-        meta.model_from_str("""start  first 
-                first second""")  # noqa
+        meta.model_from_str(
+            """start  first 
+                first second"""
+        )  # noqa
 
-    meta.model_from_str('start first first second')
+    meta.model_from_str("start first first second")
 
 
 def test_autokwd():
@@ -64,11 +68,11 @@ def test_autokwd():
     # If autokwd is disabled (default) zeroormore will
     # consume all 'first's.
     with pytest.raises(TextXSyntaxError):
-        model = meta.model_from_str('start first first firstsecond')
+        model = meta.model_from_str("start first first firstsecond")
 
     meta = metamodel_from_str(langdef, autokwd=True)
-    model = meta.model_from_str('start first first firstsecond')
+    model = meta.model_from_str("start first first firstsecond")
     assert model
-    assert hasattr(model, 'rules')
+    assert hasattr(model, "rules")
     assert len(model.rules) == 2
-    assert model.rules[1] == 'first'
+    assert model.rules[1] == "first"
diff --git a/tests/functional/test_metamodel_properties.py b/tests/functional/test_metamodel_properties.py
index 630669743..a5af4e0d5 100644
--- a/tests/functional/test_metamodel_properties.py
+++ b/tests/functional/test_metamodel_properties.py
@@ -1,9 +1,9 @@
-from __future__ import unicode_literals
 import textx
 
 
 def test_metamodel_rule_properties():
-    mm = textx.metamodel_from_str(r'''
+    mm = textx.metamodel_from_str(
+        r"""
         Model: 'value' a=C b=K c=N d=O e=P q=Q l=L;
         C: D|E;
         D: 'D';
@@ -19,27 +19,29 @@ def test_metamodel_rule_properties():
         R: E L;
         S: E L | M;
         T: M;
-    ''')
+    """
+    )
     assert mm is not None
     all_rules = mm._current_namespace
-    assert all_rules['C']._tx_type == 'match'
-    assert all_rules['D']._tx_type == 'match'
-    assert all_rules['E']._tx_type == 'match'
-    assert all_rules['F']._tx_type == 'match'
-    assert all_rules['K']._tx_type == 'abstract'
-    assert all_rules['L']._tx_type == 'common'
-    assert all_rules['M']._tx_type == 'common'
-    assert all_rules['N']._tx_type == 'abstract'
-    assert all_rules['O']._tx_type == 'abstract'
-    assert all_rules['P']._tx_type == 'match'
-    assert all_rules['Q']._tx_type == 'abstract'
-    assert all_rules['R']._tx_type == 'abstract'
-    assert all_rules['S']._tx_type == 'abstract'
-    assert all_rules['T']._tx_type == 'abstract'
+    assert all_rules["C"]._tx_type == "match"
+    assert all_rules["D"]._tx_type == "match"
+    assert all_rules["E"]._tx_type == "match"
+    assert all_rules["F"]._tx_type == "match"
+    assert all_rules["K"]._tx_type == "abstract"
+    assert all_rules["L"]._tx_type == "common"
+    assert all_rules["M"]._tx_type == "common"
+    assert all_rules["N"]._tx_type == "abstract"
+    assert all_rules["O"]._tx_type == "abstract"
+    assert all_rules["P"]._tx_type == "match"
+    assert all_rules["Q"]._tx_type == "abstract"
+    assert all_rules["R"]._tx_type == "abstract"
+    assert all_rules["S"]._tx_type == "abstract"
+    assert all_rules["T"]._tx_type == "abstract"
 
 
 def test_metamodel_abstract_rule_detail1():
-    mm = textx.metamodel_from_str(r'''
+    mm = textx.metamodel_from_str(
+        r"""
         Model: 'value' a=C b=K c=N d=O e=P q=Q l=L;
         C: D|E;
         D: 'D';
@@ -54,29 +56,31 @@ def test_metamodel_abstract_rule_detail1():
         Q: '1'|'2'|'3' L;
         R: E L;
         S: E L | M;
-    ''')
+    """
+    )
     assert mm is not None
     all_rules = mm._current_namespace
-    assert all_rules['C']._tx_type == 'match'
-    assert all_rules['D']._tx_type == 'match'
-    assert all_rules['E']._tx_type == 'match'
-    assert all_rules['F']._tx_type == 'match'
-    assert all_rules['K']._tx_type == 'abstract'
-    assert all_rules['L']._tx_type == 'common'
-    assert all_rules['M']._tx_type == 'common'
-    assert all_rules['N']._tx_type == 'abstract'
-    assert all_rules['O']._tx_type == 'abstract'
-    assert all_rules['P']._tx_type == 'match'
-    assert all_rules['Q']._tx_type == 'abstract'
-    assert all_rules['R']._tx_type == 'abstract'
-    assert all_rules['S']._tx_type == 'abstract'
+    assert all_rules["C"]._tx_type == "match"
+    assert all_rules["D"]._tx_type == "match"
+    assert all_rules["E"]._tx_type == "match"
+    assert all_rules["F"]._tx_type == "match"
+    assert all_rules["K"]._tx_type == "abstract"
+    assert all_rules["L"]._tx_type == "common"
+    assert all_rules["M"]._tx_type == "common"
+    assert all_rules["N"]._tx_type == "abstract"
+    assert all_rules["O"]._tx_type == "abstract"
+    assert all_rules["P"]._tx_type == "match"
+    assert all_rules["Q"]._tx_type == "abstract"
+    assert all_rules["R"]._tx_type == "abstract"
+    assert all_rules["S"]._tx_type == "abstract"
 
 
 def test_metamodel_abstract_rule_detail2():
     # Example with "Base: Special1| (Special2 NotSpecial3)"
     # "If there are multiple common rules than the first will be used as a result and
     # the rest only for parsing" from http://textx.github.io/textX/stable/grammar/
-    mm = textx.metamodel_from_str(r'''
+    mm = textx.metamodel_from_str(
+        r"""
         Model: 'value' x=Base;
         Base: S1 | S2 NotS3 // only use first rule (S2) as
                             // possible instance for each choice-option
@@ -88,13 +92,15 @@ def test_metamodel_abstract_rule_detail2():
         S4: name=ID;
         S5: name=ID;
         S6: name=ID;
-    ''')
+    """
+    )
     assert mm is not None
     all_rules = mm._current_namespace
-    assert all_rules['Base']._tx_type == 'abstract'
+    assert all_rules["Base"]._tx_type == "abstract"
     assert textx.textx_isinstance(mm["S1"], mm["Base"])
     assert textx.textx_isinstance(mm["S2"], mm["Base"])
     assert not textx.textx_isinstance(mm["NotS3"], mm["Base"])  # problem
 
-    assert set([c.__name__ for c in all_rules['Base']._tx_inh_by]) \
-        == set(['S1', 'S2', 'S4', 'S5'])
+    assert set([c.__name__ for c in all_rules["Base"]._tx_inh_by]) == set(
+        ["S1", "S2", "S4", "S5"]
+    )
diff --git a/tests/functional/test_model_api.py b/tests/functional/test_model_api.py
index cd8b197c3..7930986a0 100644
--- a/tests/functional/test_model_api.py
+++ b/tests/functional/test_model_api.py
@@ -1,10 +1,14 @@
 """
 Model query and navigation API.
 """
-from __future__ import unicode_literals
 import pytest  # noqa
-from textx import metamodel_from_str, get_children, get_children_of_type, \
-    get_parent_of_type, get_model
+from textx import (
+    metamodel_from_str,
+    get_children,
+    get_children_of_type,
+    get_parent_of_type,
+    get_model,
+)
 
 
 grammar = """
@@ -32,76 +36,75 @@
 
 
 def test_get_children():
-
     metamodel = metamodel_from_str(grammar)
     model = metamodel.model_from_str(model_str)
 
     seconds_thirds = get_children(
-        lambda x: x.__class__.__name__ in ['Second', 'Third'], model)
+        lambda x: x.__class__.__name__ in ["Second", "Third"], model
+    )
 
     assert len(seconds_thirds) == 8  # 3 seconds and 5 thirds
     assert seconds_thirds[0].x == [23, 45, 56]
-    assert seconds_thirds[1].x == 'one'
-    assert seconds_thirds[-1].x == 'third'
+    assert seconds_thirds[1].x == "one"
+    assert seconds_thirds[-1].x == "third"
 
     # Children first
     seconds_thirds = get_children(
-        lambda x: x.__class__.__name__ in ['Second', 'Third'], model,
-        children_first=True)
+        lambda x: x.__class__.__name__ in ["Second", "Third"], model, children_first=True
+    )
 
     assert len(seconds_thirds) == 8  # 3 seconds and 5 thirds
-    assert seconds_thirds[0].x == 'one'
+    assert seconds_thirds[0].x == "one"
     assert seconds_thirds[1].x == [23, 45, 56]
-    assert seconds_thirds[-1].x == 'third'
+    assert seconds_thirds[-1].x == "third"
 
     # Do not traverse seconds
     seconds_thirds = get_children(
-        lambda x: x.__class__.__name__ in ['Second', 'Third'], model,
-        should_follow=lambda x: x.__class__.__name__ != 'Second')
+        lambda x: x.__class__.__name__ in ["Second", "Third"],
+        model,
+        should_follow=lambda x: x.__class__.__name__ != "Second",
+    )
     assert len(seconds_thirds) == 3  # Only 3 thirds at the top of the model
-    assert seconds_thirds[0].x == 'first'
-    assert seconds_thirds[-1].x == 'third'
+    assert seconds_thirds[0].x == "first"
+    assert seconds_thirds[-1].x == "third"
 
 
 def test_get_children_of_type():
-
     metamodel = metamodel_from_str(grammar)
     model = metamodel.model_from_str(model_str)
 
-    thirds = get_children_of_type('Third', model)
+    thirds = get_children_of_type("Third", model)
     assert len(thirds) == 5
-    assert set(['first', 'second', 'third', 'one', 'two']) \
-        == set([a.x for a in thirds])
+    assert set(["first", "second", "third", "one", "two"]) == set([a.x for a in thirds])
 
     # Test search in the part of the model
-    thirds = get_children_of_type('Third', model.a[1])
+    thirds = get_children_of_type("Third", model.a[1])
     assert len(thirds) == 1
-    assert 'two' == list(thirds)[0].x
+    assert list(thirds)[0].x == "two"
 
     # Do not traverse seconds
     thirds = get_children_of_type(
-        'Third', model, should_follow=lambda x: x.__class__.__name__ != 'Second')
+        "Third", model, should_follow=lambda x: x.__class__.__name__ != "Second"
+    )
     assert len(thirds) == 3  # Only 3 thirds at the top of the model
-    assert thirds[0].x == 'first'
-    assert thirds[-1].x == 'third'
+    assert thirds[0].x == "first"
+    assert thirds[-1].x == "third"
 
 
 def test_get_parent_of_type():
-
     metamodel = metamodel_from_str(grammar)
     model = metamodel.model_from_str(model_str)
 
     t = model.a[0].y
-    s = get_parent_of_type('Second', t)
-    assert s.__class__.__name__ == 'Second'
+    s = get_parent_of_type("Second", t)
+    assert s.__class__.__name__ == "Second"
     assert s.x[0] == 23
-    f = get_parent_of_type('First', t)
-    assert f.__class__.__name__ == 'First'
+    f = get_parent_of_type("First", t)
+    assert f.__class__.__name__ == "First"
     assert f is model
 
 
 def test_get_model():
-
     metamodel = metamodel_from_str(grammar)
     model = metamodel.model_from_str(model_str)
 
diff --git a/tests/functional/test_objcrossref_positions.py b/tests/functional/test_objcrossref_positions.py
index bba569d8b..af9bce453 100644
--- a/tests/functional/test_objcrossref_positions.py
+++ b/tests/functional/test_objcrossref_positions.py
@@ -1,7 +1,7 @@
-from __future__ import unicode_literals
-from os.path import join, abspath, dirname
-from textx import metamodel_from_str
+from os.path import abspath, dirname, join
+
 import textx.scoping.providers as scoping_providers
+from textx import metamodel_from_str
 
 grammar = r"""
 Model:
@@ -21,8 +21,7 @@
 Import: 'import' importURI=FQNI;
 """
 
-model_b_file_name = join(abspath(dirname(__file__)),
-                         "test_objcrossref_positions_B.model")
+model_b_file_name = join(abspath(dirname(__file__)), "test_objcrossref_positions_B.model")
 
 model_b_str = """
 import test_objcrossref_positions
@@ -33,10 +32,9 @@
 }
 """
 
-model_a_file_name = join(abspath(dirname(__file__)),
-                         "test_objcrossref_positions.model")
+model_a_file_name = join(abspath(dirname(__file__)), "test_objcrossref_positions.model")
 
-with open(model_a_file_name, 'r') as model_a_file:
+with open(model_a_file_name) as model_a_file:
     model_a_str = model_a_file.read()
 
 
@@ -51,7 +49,8 @@ def conv(i):
         return i.replace(".", "/") + ".model"
 
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.FQNImportURI(importURI_converter=conv)})
+        {"*.*": scoping_providers.FQNImportURI(importURI_converter=conv)}
+    )
 
     #################################
     # MODEL PARSING
@@ -66,17 +65,19 @@ def conv(i):
 
     # def_pos
     rule_position = model_a_str.find("packageA1")  # look for object A in packageA1
-    assert model_a_str.find("object A", rule_position) \
+    assert (
+        model_a_str.find("object A", rule_position)
         == model_b._pos_crossref_list[0].def_pos_start
+    )
     rule_position = model_a_str.find("packageA2")  # look for object A in packageA2
-    assert model_a_str.find("object A", rule_position) \
+    assert (
+        model_a_str.find("object A", rule_position)
         == model_b._pos_crossref_list[1].def_pos_start
+    )
 
     # ref_pos
-    assert model_b_str.find("packageA1.A") \
-        == model_b._pos_crossref_list[0].ref_pos_start
-    assert model_b_str.find("packageA2.A") \
-        == model_b._pos_crossref_list[1].ref_pos_start
+    assert model_b_str.find("packageA1.A") == model_b._pos_crossref_list[0].ref_pos_start
+    assert model_b_str.find("packageA2.A") == model_b._pos_crossref_list[1].ref_pos_start
 
     # def_file_name
     assert model_a_file_name == model_b._pos_crossref_list[0].def_file_name
@@ -88,18 +89,23 @@ def conv(i):
 
     # def_pos
     rule_position = model_a_str.find("packageA1")  # look for object A in packageA1
-    assert model_a_str.find("object A", rule_position) \
+    assert (
+        model_a_str.find("object A", rule_position)
         == model_a._pos_crossref_list[0].def_pos_start
+    )
     rule_position = model_a_str.find("packageA2")  # look for object A in packageA2
-    assert model_a_str.find("object A", rule_position) \
+    assert (
+        model_a_str.find("object A", rule_position)
         == model_a._pos_crossref_list[1].def_pos_start
+    )
 
     # ref_pos
-    assert model_a_str.find("packageA1.A") \
-        == model_a._pos_crossref_list[0].ref_pos_start
+    assert model_a_str.find("packageA1.A") == model_a._pos_crossref_list[0].ref_pos_start
     rule_position = model_a_str.find("object B")  # look for object B crossref
-    assert model_a_str.find("A", rule_position) \
+    assert (
+        model_a_str.find("A", rule_position)
         == model_a._pos_crossref_list[1].ref_pos_start
+    )
 
     # def_file_name
     assert model_a_file_name == model_a._pos_crossref_list[0].def_file_name
diff --git a/tests/functional/test_processors.py b/tests/functional/test_processors.py
index ab3773da8..f133b25d6 100644
--- a/tests/functional/test_processors.py
+++ b/tests/functional/test_processors.py
@@ -1,16 +1,9 @@
 """
 Testing model and object processors.
 """
-from __future__ import unicode_literals
 import pytest  # noqa
-import sys
 from textx import metamodel_from_str, textxerror_wrap
 
-if sys.version < '3':
-    text = unicode  # noqa
-else:
-    text = str
-
 grammar = """
 First:
     'first' seconds+=Second
@@ -76,8 +69,8 @@ def second_obj_processor(second):
         assert second.parent.b
 
     obj_processors = {
-        'First': first_obj_processor,
-        'Second': second_obj_processor,
+        "First": first_obj_processor,
+        "Second": second_obj_processor,
     }
 
     metamodel = metamodel_from_str(grammar)
@@ -86,9 +79,9 @@ def second_obj_processor(second):
     model_str = 'first 34 45 7 A 45 65 B true C "dfdf"'
     first = metamodel.model_from_str(model_str)
 
-    assert hasattr(first, '_first_called')
+    assert hasattr(first, "_first_called")
     for s in first.seconds:
-        assert hasattr(s, '_second_called')
+        assert hasattr(s, "_second_called")
     assert call_order == [2, 2, 2, 1]
 
 
@@ -111,18 +104,18 @@ def second_obj_processor(second):
         assert second.parent.b is not None
 
     obj_processors = {
-        'First': first_obj_processor,
-        'Second': second_obj_processor,
+        "First": first_obj_processor,
+        "Second": second_obj_processor,
     }
 
-    class First(object):
+    class First:
         def __init__(self, seconds, a, b, c):
             self.seconds = seconds
             self.a = a
             self.b = b
             self.c = c
 
-    class Second(object):
+    class Second:
         def __init__(self, sec, parent):
             self.sec = sec
             self.parent = parent
@@ -133,10 +126,10 @@ def __init__(self, sec, parent):
     model_str = 'first 34 45 7 A 45 65 B true C "dfdf"'
     first = metamodel.model_from_str(model_str)
 
-    assert hasattr(first, '_first_called')
+    assert hasattr(first, "_first_called")
     assert first._a_copy == first.a
     for s in first.seconds:
-        assert hasattr(s, '_second_called')
+        assert hasattr(s, "_second_called")
         assert s._sec_copy == s.sec
 
 
@@ -149,10 +142,10 @@ def second_obj_processor(second):
         second._second_called = True
 
     obj_processors = {
-        'Second': second_obj_processor,
+        "Second": second_obj_processor,
     }
 
-    class Second(object):
+    class Second:
         def __init__(self, parent, sec):
             self.parent = parent
             self.sec = sec
@@ -176,15 +169,16 @@ def test_object_processor_replace_object():
     Test that what is returned from object processor is value used in the
     output model.
     """
+
     def second_obj_processor(second):
         return second.sec / 2
 
     def string_obj_processor(mystr):
-        return "[{}]".format(mystr)
+        return f"[{mystr}]"
 
     obj_processors = {
-        'Second': second_obj_processor,
-        'STRING': string_obj_processor,
+        "Second": second_obj_processor,
+        "STRING": string_obj_processor,
     }
 
     metamodel = metamodel_from_str(grammar)
@@ -208,25 +202,22 @@ def test_obj_processor_simple_match_rule():
         /\d+\.(\d+)?/
     ;
     """
-    model = '3. end'
+    model = "3. end"
 
     mm = metamodel_from_str(grammar)
     m = mm.model_from_str(model)
-    assert type(m.a) is text
+    assert isinstance(m.a, str)
 
-    processors = {
-        'MyFloat': lambda x: float(x)
-    }
-    print('filters')
+    processors = {"MyFloat": lambda x: float(x)}
+    print("filters")
     mm = metamodel_from_str(grammar)
     mm.register_obj_processors(processors)
     m = mm.model_from_str(model)
 
-    assert type(m.a) is float
+    assert isinstance(m.a, float)
 
 
 def test_obj_processor_sequence_match_rule():
-
     grammar = """
     First:
         i=MyFixedInt 'end'
@@ -236,20 +227,18 @@ def test_obj_processor_sequence_match_rule():
     ;
     """
 
-    model = '0004 end'
+    model = "0004 end"
 
     mm = metamodel_from_str(grammar)
     m = mm.model_from_str(model)
-    assert type(m.i) is text
+    assert isinstance(m.i, str)
 
-    processors = {
-        'MyFixedInt': lambda x: int(x)
-    }
+    processors = {"MyFixedInt": lambda x: int(x)}
     mm = metamodel_from_str(grammar)
     mm.register_obj_processors(processors)
     m = mm.model_from_str(model)
 
-    assert type(m.i) is int
+    assert isinstance(m.i, int)
 
 
 def test_base_type_obj_processor_override():
@@ -260,17 +249,15 @@ def test_base_type_obj_processor_override():
     """
 
     def to_float_with_str_check(x):
-        assert type(x) is text
+        assert isinstance(x, str)
         return float(x)
 
-    processors = {
-        'INT': to_float_with_str_check
-    }
+    processors = {"INT": to_float_with_str_check}
     mm = metamodel_from_str(grammar)
     mm.register_obj_processors(processors)
-    m = mm.model_from_str('begin 34 end')
+    m = mm.model_from_str("begin 34 end")
 
-    assert type(m.i) is float
+    assert isinstance(m.i, float)
 
 
 def test_custom_base_type_with_builtin_alternatives():
@@ -281,14 +268,14 @@ def test_custom_base_type_with_builtin_alternatives():
     """  # noqa
 
     mm = metamodel_from_str(grammar)
-    model = mm.model_from_str('3.4 6')
-    assert type(model.i[0]) is text
-    assert type(model.i[1]) is int
+    model = mm.model_from_str("3.4 6")
+    assert isinstance(model.i[0], str)
+    assert isinstance(model.i[1], int)
 
-    mm.register_obj_processors({'MyFloat': lambda x: float(x)})
-    model = mm.model_from_str('3.4 6')
-    assert type(model.i[0]) is float
-    assert type(model.i[1]) is int
+    mm.register_obj_processors({"MyFloat": lambda x: float(x)})
+    model = mm.model_from_str("3.4 6")
+    assert isinstance(model.i[0], float)
+    assert isinstance(model.i[1], int)
 
 
 def test_nested_match_rules():
@@ -307,25 +294,31 @@ def howmany_processor(x):
         return len(x)
 
     mm = metamodel_from_str(grammar)
-    mm.register_obj_processors({'HowMany': howmany_processor,
-                                'MyFloat': lambda x: float(x)})
-    model = mm.model_from_str('3.4 ++ + ++ 6')
+    mm.register_obj_processors(
+        {"HowMany": howmany_processor, "MyFloat": lambda x: float(x)}
+    )
+    model = mm.model_from_str("3.4 ++ + ++ 6")
     assert model.objects[0] == 3.4
     assert model.objects[1] == 5
     assert model.objects[2] == 6
-    assert type(model.objects[2]) is int
+    assert isinstance(model.objects[2], int)
 
     # Now we will add another processor for `MyObject` to test if we can change
     # the result returned from match processors lower in hierarchy.
     def myobject_processor(x):
-        assert type(x) in [int, float]
-        return '#{}'.format(text(x))
-    mm.register_obj_processors({'HowMany': howmany_processor,
-                                'MyFloat': lambda x: float(x),
-                                'MyObject': myobject_processor})
-    model = mm.model_from_str('3.4 ++ + ++ 6')
-    assert model.objects[0] == '#3.4'
-    assert model.objects[1] == '#5'
+        assert isinstance(x, (int, float))
+        return f"#{x}"
+
+    mm.register_obj_processors(
+        {
+            "HowMany": howmany_processor,
+            "MyFloat": lambda x: float(x),
+            "MyObject": myobject_processor,
+        }
+    )
+    model = mm.model_from_str("3.4 ++ + ++ 6")
+    assert model.objects[0] == "#3.4"
+    assert model.objects[1] == "#5"
 
 
 def test_multipart_nested_match_rules():
@@ -345,11 +338,11 @@ def myfloat_processor(x):
         return float(x)
 
     mm = metamodel_from_str(grammar)
-    mm.register_obj_processors({'MyFloat': myfloat_processor})
-    model = mm.model_from_str(' # 3.4 -- 6 -- ')
+    mm.register_obj_processors({"MyFloat": myfloat_processor})
+    model = mm.model_from_str(" # 3.4 -- 6 -- ")
     assert called[0]
-    assert model.objects[0] == '#3.4'
-    assert model.objects[1] == '--6--'
+    assert model.objects[0] == "#3.4"
+    assert model.objects[1] == "--6--"
 
 
 def test_obj_processor_exception_wrap_for_external_exceptions():
@@ -361,14 +354,16 @@ def test_obj_processor_exception_wrap_for_external_exceptions():
     @textxerror_wrap
     def date_converter(model_date):
         from datetime import datetime
+
         return datetime(day=model_date.d, month=model_date.m, year=model_date.y)
 
     mm = metamodel_from_str(grammar)
-    mm.register_obj_processors({'Date': date_converter})
-    mm.model_from_str('1.12.20')  # ok
+    mm.register_obj_processors({"Date": date_converter})
+    mm.model_from_str("1.12.20")  # ok
     from textx import TextXError
-    with pytest.raises(TextXError, match=r'None:1:1:.*1\.\.12'):
-        mm.model_from_str('1.13.21')  # month>12
+
+    with pytest.raises(TextXError, match=r"None:1:1:.*1\.\.12"):
+        mm.model_from_str("1.13.21")  # month>12
 
 
 def test_obj_processor_exception_wrap_for_common_rules():
@@ -379,15 +374,16 @@ def test_obj_processor_exception_wrap_for_common_rules():
 
     @textxerror_wrap
     def p(a):
-        if a.name == 'E':
+        if a.name == "E":
             raise Exception("test")
 
     mm = metamodel_from_str(grammar)
-    mm.register_obj_processors({'A': p})
+    mm.register_obj_processors({"A": p})
     from textx.exceptions import TextXError
-    mm.model_from_str('X Y Z (X)')
-    with pytest.raises(TextXError, match=r'None:1:3:.*test'):
-        mm.model_from_str('X E Z (X)')
+
+    mm.model_from_str("X Y Z (X)")
+    with pytest.raises(TextXError, match=r"None:1:3:.*test"):
+        mm.model_from_str("X E Z (X)")
 
 
 def test_obj_processor_exception_wrap_for_match_rules():
@@ -398,12 +394,13 @@ def test_obj_processor_exception_wrap_for_match_rules():
 
     @textxerror_wrap
     def p(a):
-        if a == 'E':
+        if a == "E":
             raise Exception("test")
 
     mm = metamodel_from_str(grammar)
-    mm.register_obj_processors({'A': p})
+    mm.register_obj_processors({"A": p})
     from textx.exceptions import TextXError
-    mm.model_from_str('X Y Z')
-    with pytest.raises(TextXError, match=r'None:1:3:.*test'):
-        mm.model_from_str('X E Z')
+
+    mm.model_from_str("X Y Z")
+    with pytest.raises(TextXError, match=r"None:1:3:.*test"):
+        mm.model_from_str("X E Z")
diff --git a/tests/functional/test_regexp_with_groups.py b/tests/functional/test_regexp_with_groups.py
index 79c7e3a0b..51dd5f7bd 100644
--- a/tests/functional/test_regexp_with_groups.py
+++ b/tests/functional/test_regexp_with_groups.py
@@ -1,16 +1,9 @@
 """
 Testing model and regexp with groups.
 """
-from __future__ import unicode_literals
 import pytest  # noqa
-import sys
 from textx import metamodel_from_str
 
-if sys.version < '3':
-    text = unicode  # noqa
-else:
-    text = str
-
 grammar = r"""
 Model: entries += Entry;
 Entry:
@@ -39,8 +32,8 @@ def test_regexp_with_groups_deactivated():
     m = metamodel.model_from_str(model_str)
 
     assert '"""' in m.entries[0].data  # """ is not removed
-    assert 'This' in m.entries[0].data  # This and text in model
-    assert 'text!' in m.entries[0].data  # This and text in model
+    assert "This" in m.entries[0].data  # This and text in model
+    assert "text!" in m.entries[0].data  # This and text in model
 
 
 def test_regexp_with_groups_activated():
@@ -59,8 +52,8 @@ def test_regexp_with_groups_activated():
     m = metamodel.model_from_str(model_str)
 
     assert '"""' not in m.entries[0].data  # """ is not removed
-    assert 'This' in m.entries[0].data  # This and text in model
-    assert 'text!' in m.entries[0].data  # This and text in model
+    assert "This" in m.entries[0].data  # This and text in model
+    assert "text!" in m.entries[0].data  # This and text in model
 
 
 def test_regexp_with_groups_activated2():
@@ -73,4 +66,4 @@ def test_regexp_with_groups_activated2():
     metamodel = metamodel_from_str(grammar2, use_regexp_group=True)
     m = metamodel.model_from_str(model_str)
 
-    assert '"""This is a multiline"""-ExtraInfo-' == m.data
+    assert m.data == '"""This is a multiline"""-ExtraInfo-'
diff --git a/tests/functional/test_repeat_modifiers.py b/tests/functional/test_repeat_modifiers.py
index 7c483f6cf..d1f1f31e3 100644
--- a/tests/functional/test_repeat_modifiers.py
+++ b/tests/functional/test_repeat_modifiers.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
 import pytest
-from textx import metamodel_from_str, TextXSyntaxError
+
+from textx import TextXSyntaxError, metamodel_from_str
 
 
 def test_modifier_separator_zeroormore():
@@ -114,7 +114,7 @@ def test_assignment_modifier_separator_zeroormore():
     model = metamodel.model_from_str("a,b, a")
     # 3 AorBs must be matched
     assert len(model.a) == 3
-    assert model.a[1] == 'b'
+    assert model.a[1] == "b"
 
 
 def test_assignment_modifier_separator_oneormore():
@@ -129,7 +129,7 @@ def test_assignment_modifier_separator_oneormore():
     model = metamodel.model_from_str("a,b, a")
     # 3 AorBs must be matched
     assert len(model.a) == 3
-    assert model.a[1] == 'b'
+    assert model.a[1] == "b"
 
 
 def test_assignment_modifier_separator_optional():
@@ -171,18 +171,22 @@ def test_modifier_eolterm_zeroormore():
     # After 'first' and before newline must
     # be one or more integers
     with pytest.raises(TextXSyntaxError):
-        model = metamodel.model_from_str("""
+        model = metamodel.model_from_str(
+            """
             first
             34 56 88 65
             123456
-        """)
+        """
+        )
 
     # When newline is found matching integers
     # finishes and than a '123456' is matched
-    model = metamodel.model_from_str("""
+    model = metamodel.model_from_str(
+        """
         first 34 56 88 65
         123456
-    """)
+    """
+    )
     assert model
 
 
@@ -197,18 +201,22 @@ def test_modifier_eolterm_oneormore():
     # After 'first' and before newline must
     # be one or more integers
     with pytest.raises(TextXSyntaxError):
-        model = metamodel.model_from_str("""
+        model = metamodel.model_from_str(
+            """
             first
             34 56 88 65
             123456
-        """)
+        """
+        )
 
     # When newline is found matching integers
     # finishes and than a '123456' is matched
-    model = metamodel.model_from_str("""
+    model = metamodel.model_from_str(
+        """
         first 34 56 88 65
         123456
-    """)
+    """
+    )
     assert model
 
 
@@ -226,16 +234,20 @@ def test_multiple_modifiers():
     # After 'first' and before newline must
     # be one or more integers separated with comma
     with pytest.raises(TextXSyntaxError):
-        model = metamodel.model_from_str("""
+        model = metamodel.model_from_str(
+            """
             first
             34 56 88 65
             123456
-        """)
+        """
+        )
 
     # When newline is found matching integers
     # finishes and than a '123456' is matched
-    model = metamodel.model_from_str("""
+    model = metamodel.model_from_str(
+        """
         first 34, 56, 88, 65
         123456
-    """)
+    """
+    )
     assert model
diff --git a/tests/functional/test_resolving.py b/tests/functional/test_resolving.py
index f0048db9c..3cb2b75ee 100644
--- a/tests/functional/test_resolving.py
+++ b/tests/functional/test_resolving.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 from textx import metamodel_from_str
 from arpeggio import Sequence, OrderedChoice
@@ -15,10 +14,12 @@ def test_match_single_peg_rule_resolve():
     Rule3: 'a' INT;
     """
     metamodel = metamodel_from_str(model)
-    assert metamodel['Rule1']._tx_peg_rule == \
-        metamodel['Rule2']._tx_peg_rule == \
-        metamodel['Rule3']._tx_peg_rule
-    assert type(metamodel['Rule1']._tx_peg_rule) is Sequence
+    assert (
+        metamodel["Rule1"]._tx_peg_rule
+        == metamodel["Rule2"]._tx_peg_rule
+        == metamodel["Rule3"]._tx_peg_rule
+    )
+    assert type(metamodel["Rule1"]._tx_peg_rule) is Sequence
 
 
 def test_match_complex_recursive_peg_rule_resolve():
@@ -35,21 +36,20 @@ def test_match_complex_recursive_peg_rule_resolve():
     """
     metamodel = metamodel_from_str(grammar)
 
-    assert metamodel._parser_blueprint.parser_model.nodes[0].rule_name == \
-        'expression'
+    assert metamodel._parser_blueprint.parser_model.nodes[0].rule_name == "expression"
     assert type(metamodel._parser_blueprint.parser_model.nodes[0]) is Sequence
 
-    calc_rule = metamodel['calc']._tx_peg_rule
-    expression_rule = metamodel['expression']._tx_peg_rule
+    calc_rule = metamodel["calc"]._tx_peg_rule
+    expression_rule = metamodel["expression"]._tx_peg_rule
     assert calc_rule is expression_rule
     assert type(calc_rule) is Sequence
 
-    assert type(metamodel['term_op']._tx_peg_rule) is OrderedChoice
+    assert type(metamodel["term_op"]._tx_peg_rule) is OrderedChoice
 
     # Recursive factor rule
-    factor_rule = metamodel['factor']._tx_peg_rule
+    factor_rule = metamodel["factor"]._tx_peg_rule
     # Find expression reference
     expr_ref = factor_rule.nodes[1].nodes[1]
-    assert expr_ref.rule_name == 'expression'
+    assert expr_ref.rule_name == "expression"
     assert type(expr_ref) is Sequence
     assert expr_ref is expression_rule
diff --git a/tests/functional/test_rule_modifiers.py b/tests/functional/test_rule_modifiers.py
index 7d0a25fa8..d544f4a9a 100644
--- a/tests/functional/test_rule_modifiers.py
+++ b/tests/functional/test_rule_modifiers.py
@@ -1,5 +1,5 @@
-from __future__ import unicode_literals
 import pytest
+
 from textx import metamodel_from_str
 from textx.exceptions import TextXSyntaxError
 
diff --git a/tests/functional/test_scoping/importURI_variations/test_importURI_variations.py b/tests/functional/test_scoping/importURI_variations/test_importURI_variations.py
index 125fb1430..0f7ada4e2 100644
--- a/tests/functional/test_scoping/importURI_variations/test_importURI_variations.py
+++ b/tests/functional/test_scoping/importURI_variations/test_importURI_variations.py
@@ -1,12 +1,12 @@
-from __future__ import unicode_literals
+from os.path import abspath, dirname, join
 
-from textx import metamodel_from_str
-from os.path import dirname, abspath, join
 from pytest import raises
+
 import textx.exceptions
 import textx.scoping.providers as scoping_providers
+from textx import metamodel_from_str
 
-grammar = r'''
+grammar = r"""
 Model:
         imports*=Import
         packages*=Package
@@ -26,7 +26,7 @@
 FQNI: ID+['.']('.*')?;
 Import: 'import' importURI=FQNI ('as' name=ID)?;
 Comment: /\/\/.*$/;
-'''
+"""
 
 
 def test_importURI_variations_import_string_hook():
@@ -40,14 +40,16 @@ def conv(i):
         return i.replace(".", "/") + ".model"
 
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.FQNImportURI(importURI_converter=conv)})
+        {"*.*": scoping_providers.FQNImportURI(importURI_converter=conv)}
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)), "importStringHook", "b.model"))
+        join(abspath(dirname(__file__)), "importStringHook", "b.model")
+    )
 
     #################################
     # TEST MODEL
@@ -75,15 +77,16 @@ def conv(i):
         return i.replace(".", "/") + ".model"
 
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.FQNImportURI(importURI_converter=conv,
-                                               importAs=True)})
+        {"*.*": scoping_providers.FQNImportURI(importURI_converter=conv, importAs=True)}
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)), "importAs", "b_ok1.model"))
+        join(abspath(dirname(__file__)), "importAs", "b_ok1.model")
+    )
 
     #################################
     # TEST MODEL
@@ -111,15 +114,16 @@ def conv(i):
         return i.replace(".", "/") + ".model"
 
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.FQNImportURI(importURI_converter=conv,
-                                               importAs=True)})
+        {"*.*": scoping_providers.FQNImportURI(importURI_converter=conv, importAs=True)}
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)), "importAs", "b_ok2.model"))
+        join(abspath(dirname(__file__)), "importAs", "b_ok2.model")
+    )
 
     #################################
     # TEST MODEL
@@ -147,15 +151,16 @@ def conv(i):
         return i.replace(".", "/") + ".model"
 
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.FQNImportURI(importURI_converter=conv,
-                                               importAs=True)})
+        {"*.*": scoping_providers.FQNImportURI(importURI_converter=conv, importAs=True)}
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)), "importAs", "b_multi_import.model"))
+        join(abspath(dirname(__file__)), "importAs", "b_multi_import.model")
+    )
 
     #################################
     # TEST MODEL
@@ -183,17 +188,19 @@ def conv(i):
         return i.replace(".", "/") + ".model"
 
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.FQNImportURI(importURI_converter=conv,
-                                               importAs=True)})
+        {"*.*": scoping_providers.FQNImportURI(importURI_converter=conv, importAs=True)}
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*Unknown object.*packageA1.A.*'):
+    with raises(
+        textx.exceptions.TextXSemanticError, match=r".*Unknown object.*packageA1.A.*"
+    ):
         my_meta_model.model_from_file(
-            join(abspath(dirname(__file__)), "importAs", "b_error.model"))
+            join(abspath(dirname(__file__)), "importAs", "b_error.model")
+        )
 
     #################################
     # END
diff --git a/tests/functional/test_scoping/test_buildins.py b/tests/functional/test_scoping/test_buildins.py
index efb28e45c..7de33bc7e 100644
--- a/tests/functional/test_scoping/test_buildins.py
+++ b/tests/functional/test_scoping/test_buildins.py
@@ -1,11 +1,10 @@
-from __future__ import unicode_literals
 from pytest import raises
 
 import textx.exceptions
 import textx.scoping.providers as scoping_providers
 from textx import metamodel_from_str
 
-metamodel_str = '''
+metamodel_str = """
 Model:
     persons*=Person
     things+=Thing
@@ -19,12 +18,12 @@
     '}'
 ;
 
-'''
+"""
 
 
-class Thing(object):
+class Thing:
     def __init__(self, **kwargs):
-        for k in kwargs.keys():
+        for k in kwargs:
             setattr(self, k, kwargs[k])
 
 
@@ -44,17 +43,19 @@ def test_buildins():
     #################################
 
     type_builtins = {
-        'OneThing': Thing(name="OneThing"),
-        'OtherThing': Thing(name="OtherThing")
+        "OneThing": Thing(name="OneThing"),
+        "OtherThing": Thing(name="OtherThing"),
     }
-    my_metamodel = metamodel_from_str(metamodel_str, classes=[Thing],
-                                      builtins=type_builtins)
+    my_metamodel = metamodel_from_str(
+        metamodel_str, classes=[Thing], builtins=type_builtins
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
-    my_metamodel.model_from_str('''
+    my_metamodel.model_from_str(
+        """
     person P
     person I
     thing A {}
@@ -62,9 +63,11 @@ def test_buildins():
     thing C {A B}
     ref P
     ref I
-    ''')
+    """
+    )
 
-    my_metamodel.model_from_str('''
+    my_metamodel.model_from_str(
+        """
     person P
     person I
     thing A {}
@@ -72,19 +75,26 @@ def test_buildins():
     thing C {A B OneThing OtherThing}
     ref P
     ref I
-    ''')
+    """
+    )
 
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*Unknown object.*UnknownPart.*'):
-        my_metamodel.model_from_str('''
+    with raises(
+        textx.exceptions.TextXSemanticError, match=r".*Unknown object.*UnknownPart.*"
+    ):
+        my_metamodel.model_from_str(
+            """
         thing A {}
         thing B {}
         thing C {A B OneThing OtherThing UnknownPart}
-        ''')
-
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*Unknown object "OneThing" of class "Person".*'):
-        my_metamodel.model_from_str('''
+        """
+        )
+
+    with raises(
+        textx.exceptions.TextXSemanticError,
+        match=r'.*Unknown object "OneThing" of class "Person".*',
+    ):
+        my_metamodel.model_from_str(
+            """
             person P
             person I
             thing A {}
@@ -92,7 +102,8 @@ def test_buildins():
             thing C {A B OneThing OtherThing}
             ref OneThing
             ref OtherThing
-        ''')
+        """
+        )
 
     #################################
     # END
@@ -116,18 +127,20 @@ def test_buildins_fully_qualified_name():
     #################################
 
     type_builtins = {
-        'OneThing': Thing(name="OneThing"),
-        'OtherThing': Thing(name="OtherThing")
+        "OneThing": Thing(name="OneThing"),
+        "OtherThing": Thing(name="OtherThing"),
     }
-    my_metamodel = metamodel_from_str(metamodel_str, classes=[Thing],
-                                      builtins=type_builtins)
+    my_metamodel = metamodel_from_str(
+        metamodel_str, classes=[Thing], builtins=type_builtins
+    )
     my_metamodel.register_scope_providers({"*.*": scoping_providers.FQN()})
 
     #################################
     # MODEL PARSING
     #################################
 
-    my_metamodel.model_from_str('''
+    my_metamodel.model_from_str(
+        """
     person P
     person I
     thing A {}
@@ -135,9 +148,11 @@ def test_buildins_fully_qualified_name():
     thing C {A B}
     ref P
     ref I
-    ''')
+    """
+    )
 
-    my_metamodel.model_from_str('''
+    my_metamodel.model_from_str(
+        """
     person P
     person I
     thing A {}
@@ -145,19 +160,26 @@ def test_buildins_fully_qualified_name():
     thing C {A B OneThing OtherThing}
     ref P
     ref I
-    ''')
+    """
+    )
 
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*Unknown object.*UnknownPart.*'):
-        my_metamodel.model_from_str('''
+    with raises(
+        textx.exceptions.TextXSemanticError, match=r".*Unknown object.*UnknownPart.*"
+    ):
+        my_metamodel.model_from_str(
+            """
         thing A {}
         thing B {}
         thing C {A B OneThing OtherThing UnknownPart}
-        ''')
-
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*Unknown object "OneThing" of class "Person".*'):
-        my_metamodel.model_from_str('''
+        """
+        )
+
+    with raises(
+        textx.exceptions.TextXSemanticError,
+        match=r'.*Unknown object "OneThing" of class "Person".*',
+    ):
+        my_metamodel.model_from_str(
+            """
             person P
             person I
             thing A {}
@@ -165,7 +187,8 @@ def test_buildins_fully_qualified_name():
             thing C {A B OneThing OtherThing}
             ref OneThing
             ref OtherThing
-        ''')
+        """
+        )
 
     #################################
     # END
diff --git a/tests/functional/test_scoping/test_builtin_models.py b/tests/functional/test_scoping/test_builtin_models.py
index f672fc1c1..d5a85bbb5 100644
--- a/tests/functional/test_scoping/test_builtin_models.py
+++ b/tests/functional/test_scoping/test_builtin_models.py
@@ -1,15 +1,14 @@
-from __future__ import unicode_literals
-
-from textx.scoping import ModelRepository
 from textx import metamodel_from_str, register_language
+from textx.scoping import ModelRepository
 
-
-types_mm = metamodel_from_str(r'''
+types_mm = metamodel_from_str(
+    r"""
 Model: types+=BaseType;
 BaseType: 'type' name=ID;
-''')
+"""
+)
 
-entity_mm_str = r'''
+entity_mm_str = r"""
 reference builtin_types as t
 Model: entities+=Entity;
 Entity: 'entity' name=ID '{'
@@ -17,22 +16,23 @@
         '}'
 ;
 Property: name=ID ':' type=[t.BaseType:ID|+m:types];
-'''
+"""
 
 
 def test_builtin_models_are_searched_by_rrel():
-
-    register_language('builtin_types', '*.type', metamodel=types_mm)
+    register_language("builtin_types", "*.type", metamodel=types_mm)
 
     builtin_models = ModelRepository()
-    builtin_models.add_model(types_mm.model_from_str('type int type bool'))
+    builtin_models.add_model(types_mm.model_from_str("type int type bool"))
 
     mm = metamodel_from_str(entity_mm_str, builtin_models=builtin_models)
 
-    model = mm.model_from_str(r'''
+    model = mm.model_from_str(
+        r"""
     entity First {
         first : bool
     }
-    ''')
-    assert model.entities[0].properties[0].type.__class__.__name__ == 'BaseType'
-    assert model.entities[0].properties[0].type.name == 'bool'
+    """
+    )
+    assert model.entities[0].properties[0].type.__class__.__name__ == "BaseType"
+    assert model.entities[0].properties[0].type.name == "bool"
diff --git a/tests/functional/test_scoping/test_children.py b/tests/functional/test_scoping/test_children.py
index 80e0c29a3..720c0489b 100644
--- a/tests/functional/test_scoping/test_children.py
+++ b/tests/functional/test_scoping/test_children.py
@@ -1,13 +1,10 @@
-from __future__ import unicode_literals
-
 import re
 from operator import eq
 
 import textx.scoping.providers as scoping_providers
-from textx import get_children, get_children_of_type
-from textx import metamodel_from_str
+from textx import get_children, get_children_of_type, metamodel_from_str
 
-metamodel_str = '''
+metamodel_str = """
 Model:
     packages*=Package
 ;
@@ -29,7 +26,7 @@
 ;
 
 FQN: ID('.'ID)*;
-    '''
+    """
 
 
 def test_children():
@@ -48,7 +45,8 @@ def test_children():
     # MODEL PARSING
     #################################
 
-    my_model = my_metamodel.model_from_str('''
+    my_model = my_metamodel.model_from_str(
+        """
     package P1 {
         class Part1 {
         }
@@ -63,7 +61,8 @@ class C2 {
             attr P2.Part2 p2b;
         }
     }
-    ''')
+    """
+    )
 
     #################################
     # TEST
@@ -73,25 +72,23 @@ class C2 {
     res.sort(key=lambda x: x.name)
     assert len(res) == 3
     assert all(map(eq, map(lambda x: x.name, res), ["C2", "Part1", "Part2"]))
-    assert not all(map(eq, map(lambda x: x.name, res),
-                       ["Part1", "Part2", "C2"]))
+    assert not all(map(eq, map(lambda x: x.name, res), ["Part1", "Part2", "C2"]))
     for x in res:
         assert x.__class__.__name__ == "Class"
 
     res = get_children_of_type("Attribute", my_model)
     res.sort(key=lambda x: x.name)
     assert len(res) == 4
-    assert all(map(eq, map(lambda x: x.name, res),
-                   ["p1", "p2a", "p2b", "rec"]))
+    assert all(map(eq, map(lambda x: x.name, res), ["p1", "p2a", "p2b", "rec"]))
     for x in res:
         assert x.__class__.__name__ == "Attribute"
 
-    res = get_children(lambda x: hasattr(x, "name") and re.match(
-        ".*2.*", x.name), my_model)
+    res = get_children(
+        lambda x: hasattr(x, "name") and re.match(".*2.*", x.name), my_model
+    )
     res.sort(key=lambda x: x.name)
     assert len(res) == 5
-    assert all(map(eq, map(lambda x: x.name, res),
-                   ["C2", "P2", "Part2", "p2a", "p2b"]))
+    assert all(map(eq, map(lambda x: x.name, res), ["C2", "P2", "Part2", "p2a", "p2b"]))
 
     #################################
     # END
diff --git a/tests/functional/test_scoping/test_exception_from_included_model.py b/tests/functional/test_scoping/test_exception_from_included_model.py
index 7292ae999..9591ff7ed 100644
--- a/tests/functional/test_scoping/test_exception_from_included_model.py
+++ b/tests/functional/test_scoping/test_exception_from_included_model.py
@@ -1,12 +1,14 @@
-from __future__ import unicode_literals
-
-from os.path import dirname, abspath, join
+from os.path import abspath, dirname, join
 
 from pytest import raises
 
 import textx.scoping.providers as scoping_providers
-from textx import (metamodel_from_file, register_language,
-                   clear_language_registrations, get_location)
+from textx import (
+    clear_language_registrations,
+    get_location,
+    metamodel_from_file,
+    register_language,
+)
 
 
 def test_exception_from_included_model():
@@ -21,20 +23,21 @@ def test_exception_from_included_model():
     this_folder = dirname(abspath(__file__))
 
     def get_meta_model(provider, grammar_file_name):
-        mm = metamodel_from_file(join(this_folder, grammar_file_name),
-                                 debug=False)
-        mm.register_scope_providers({
-            "*.*": provider,
-            "Call.method": scoping_providers.ExtRelativeName("obj.ref",
-                                                             "methods",
-                                                             "extends")
-        })
+        mm = metamodel_from_file(join(this_folder, grammar_file_name), debug=False)
+        mm.register_scope_providers(
+            {
+                "*.*": provider,
+                "Call.method": scoping_providers.ExtRelativeName(
+                    "obj.ref", "methods", "extends"
+                ),
+            }
+        )
 
         def my_processor(m):
             from textx.exceptions import TextXSemanticError
+
             if m.name == "d1":
-                raise TextXSemanticError("d1 triggers artifical error",
-                                         **get_location(m))
+                raise TextXSemanticError("d1 triggers artifical error", **get_location(m))
 
         mm.register_obj_processors({"Method": my_processor})
         return mm
@@ -42,42 +45,32 @@ def my_processor(m):
     import_lookup_provider = scoping_providers.FQNImportURI()
 
     a_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "A.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "A.tx")
+    )
     b_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "B.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "B.tx")
+    )
     c_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "C.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "C.tx")
+    )
 
     clear_language_registrations()
-    register_language(
-        'a-dsl',
-        pattern='*.a',
-        description='Test Lang A',
-        metamodel=a_mm)
-    register_language(
-        'b-dsl',
-        pattern='*.b',
-        description='Test Lang B',
-        metamodel=b_mm)
-    register_language(
-        'c-dsl',
-        pattern='*.c',
-        description='Test Lang C',
-        metamodel=c_mm)
+    register_language("a-dsl", pattern="*.a", description="Test Lang A", metamodel=a_mm)
+    register_language("b-dsl", pattern="*.b", description="Test Lang B", metamodel=b_mm)
+    register_language("c-dsl", pattern="*.c", description="Test Lang C", metamodel=c_mm)
 
     #################################
     # MODEL PARSING / TEST
     #################################
     import textx.exceptions
 
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*model_d\.b:5:3:.*d1 triggers artifical error'):
+    with raises(
+        textx.exceptions.TextXSemanticError,
+        match=r".*model_d\.b:5:3:.*d1 triggers artifical error",
+    ):
         a_mm.model_from_file(
-            join(this_folder, "metamodel_provider3",
-                 "inheritance2", "model_a.a"))
+            join(this_folder, "metamodel_provider3", "inheritance2", "model_a.a")
+        )
 
     #################################
     # END
diff --git a/tests/functional/test_scoping/test_full_qualified_name.py b/tests/functional/test_scoping/test_full_qualified_name.py
index 7785b2309..b67e6fe32 100644
--- a/tests/functional/test_scoping/test_full_qualified_name.py
+++ b/tests/functional/test_scoping/test_full_qualified_name.py
@@ -1,14 +1,12 @@
-from __future__ import unicode_literals
 from os.path import dirname, join
 
 from pytest import raises
 
 import textx.exceptions
 import textx.scoping.providers as scoping_providers
-from textx import get_children
-from textx import metamodel_from_str
+from textx import get_children, metamodel_from_str
 
-metamodel_str = '''
+metamodel_str = """
 Model:
     packages*=Package
 ;
@@ -31,7 +29,7 @@
 
 Comment: /#.*/;
 FQN: ID('.'ID)*;
-'''
+"""
 
 
 def test_fully_qualified_name_ref():
@@ -50,7 +48,8 @@ def test_fully_qualified_name_ref():
     # MODEL PARSING
     #################################
 
-    my_model = my_metamodel.model_from_str('''
+    my_model = my_metamodel.model_from_str(
+        """
     package P1 {
         class Part1 {
         }
@@ -65,35 +64,32 @@ class C2 {
             attr P2.Part2 p2b;
         }
     }
-    ''')
+    """
+    )
 
     #################################
     # TEST MODEL
     #################################
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "rec",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "rec", my_model)
     assert len(a) == 1
     assert a[0].name == "rec"
     assert a[0].ref.__class__.__name__ == "Class"
     assert a[0].ref.name == "C2"
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "p1",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "p1", my_model)
     assert len(a) == 1
     assert a[0].name == "p1"
     assert a[0].ref.__class__.__name__ == "Class"
     assert a[0].ref.name == "Part1"
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "p2a",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "p2a", my_model)
     assert len(a) == 1
     assert a[0].name == "p2a"
     assert a[0].ref.__class__.__name__ == "Class"
     assert a[0].ref.name == "Part2"
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "p2b",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "p2b", my_model)
     assert len(a) == 1
     assert a[0].name == "p2b"
     assert a[0].ref.__class__.__name__ == "Class"
@@ -102,9 +98,11 @@ class C2 {
     ###########################
     # MODEL WITH ERROR
     ############################
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'None:8:.*: Unknown object.*Part1.*'):
-        my_metamodel.model_from_str(''' #1
+    with raises(
+        textx.exceptions.TextXSemanticError, match=r"None:8:.*: Unknown object.*Part1.*"
+    ):
+        my_metamodel.model_from_str(
+            """ #1
         package P1 { #2
             class Part1 { #3
             } #4
@@ -114,14 +112,17 @@ class C2 { #7
                 attr Part1 p1; #8
             }
         }
-        ''')
-
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*test_fully_qualified_name_test_error.model:8:\d+:'
-                      ' Unknown object.*Part1.*'):
+        """
+        )
+
+    with raises(
+        textx.exceptions.TextXSemanticError,
+        match=r".*test_fully_qualified_name_test_error.model:8:\d+:"
+        " Unknown object.*Part1.*",
+    ):
         my_metamodel.model_from_file(
-            join(dirname(__file__),
-                 "misc", "test_fully_qualified_name_test_error.model"))
+            join(dirname(__file__), "misc", "test_fully_qualified_name_test_error.model")
+        )
 
     #################################
     # END
@@ -144,9 +145,9 @@ def test_fully_qualified_name_ref_type_error():
     # MODEL PARSING
     #################################
 
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*p1.*'):
-        my_metamodel.model_from_str('''
+    with raises(textx.exceptions.TextXSemanticError, match=r".*p1.*"):
+        my_metamodel.model_from_str(
+            """
         package P1 {
             class Part1 {
             }
@@ -161,4 +162,5 @@ class C2 {
                 attr p1 p2b;
             }
         }
-        ''')
+        """
+        )
diff --git a/tests/functional/test_scoping/test_full_qualified_name_rrel_manual.py b/tests/functional/test_scoping/test_full_qualified_name_rrel_manual.py
index 31aef9254..d63b6a520 100644
--- a/tests/functional/test_scoping/test_full_qualified_name_rrel_manual.py
+++ b/tests/functional/test_scoping/test_full_qualified_name_rrel_manual.py
@@ -1,13 +1,11 @@
-from __future__ import unicode_literals
 from os.path import dirname, join
 
 from pytest import raises
 
 import textx.exceptions
-from textx import get_children
-from textx import metamodel_from_str
+from textx import get_children, metamodel_from_str
 
-metamodel_str = '''
+metamodel_str = """
 Model:
     packages*=Package
 ;
@@ -30,7 +28,7 @@
 
 Comment: /#.*/;
 FQN: ID('.'ID)*;
-'''
+"""
 
 
 def test_fully_qualified_name_ref():
@@ -49,7 +47,8 @@ def test_fully_qualified_name_ref():
     # MODEL PARSING
     #################################
 
-    my_model = my_metamodel.model_from_str('''
+    my_model = my_metamodel.model_from_str(
+        """
     package P1 {
         class Part1 {
         }
@@ -64,35 +63,32 @@ class C2 {
             attr P2.Part2 p2b;
         }
     }
-    ''')
+    """
+    )
 
     #################################
     # TEST MODEL
     #################################
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "rec",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "rec", my_model)
     assert len(a) == 1
     assert a[0].name == "rec"
     assert a[0].ref.__class__.__name__ == "Class"
     assert a[0].ref.name == "C2"
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "p1",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "p1", my_model)
     assert len(a) == 1
     assert a[0].name == "p1"
     assert a[0].ref.__class__.__name__ == "Class"
     assert a[0].ref.name == "Part1"
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "p2a",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "p2a", my_model)
     assert len(a) == 1
     assert a[0].name == "p2a"
     assert a[0].ref.__class__.__name__ == "Class"
     assert a[0].ref.name == "Part2"
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "p2b",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "p2b", my_model)
     assert len(a) == 1
     assert a[0].name == "p2b"
     assert a[0].ref.__class__.__name__ == "Class"
@@ -101,9 +97,11 @@ class C2 {
     ###########################
     # MODEL WITH ERROR
     ############################
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'None:8:.*: Unknown object.*Part1.*'):
-        my_metamodel.model_from_str(''' #1
+    with raises(
+        textx.exceptions.TextXSemanticError, match=r"None:8:.*: Unknown object.*Part1.*"
+    ):
+        my_metamodel.model_from_str(
+            """ #1
         package P1 { #2
             class Part1 { #3
             } #4
@@ -113,14 +111,17 @@ class C2 { #7
                 attr Part1 p1; #8
             }
         }
-        ''')
-
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*test_fully_qualified_name_test_error.model:8:\d+:'
-                      ' Unknown object.*Part1.*'):
+        """
+        )
+
+    with raises(
+        textx.exceptions.TextXSemanticError,
+        match=r".*test_fully_qualified_name_test_error.model:8:\d+:"
+        " Unknown object.*Part1.*",
+    ):
         my_metamodel.model_from_file(
-            join(dirname(__file__),
-                 "misc", "test_fully_qualified_name_test_error.model"))
+            join(dirname(__file__), "misc", "test_fully_qualified_name_test_error.model")
+        )
 
     #################################
     # END
@@ -135,14 +136,16 @@ def test_fully_qualified_name_ref_with_splitstring():
     # META MODEL DEF
     #################################
 
-    my_metamodel = metamodel_from_str(r'''
+    my_metamodel = metamodel_from_str(
+        r"""
         Model: packages*=Package;
         Package: 'package' name=ID '{' classes*=Class '}';
         Class: 'class' name=ID '{'attributes*=Attribute'}';
         Attribute: 'attr' ref=[Class:FQN] name=ID ';';
         Comment: /#.*/;
         FQN[split='/']: ID('/'ID)*;
-    ''')
+    """
+    )
 
     my_metamodel.register_scope_providers({"Attribute.ref": "^packages*.classes"})
 
@@ -150,7 +153,8 @@ def test_fully_qualified_name_ref_with_splitstring():
     # MODEL PARSING
     #################################
 
-    my_model = my_metamodel.model_from_str('''
+    my_model = my_metamodel.model_from_str(
+        """
     package P1 {
         class Part1 {
         }
@@ -165,14 +169,14 @@ class C2 {
             attr P2/Part2 p2b;
         }
     }
-    ''')
+    """
+    )
 
     #################################
     # TEST MODEL
     #################################
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "rec",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "rec", my_model)
     assert len(a) == 1
     assert a[0].name == "rec"
     assert a[0].ref.__class__.__name__ == "Class"
@@ -180,19 +184,26 @@ class C2 {
 
 
 def test_split_param_without_delimiter():
-    with raises(textx.exceptions.TextXError,
-                match=r'.*split requires a string parameter.*'):
-        metamodel_from_str('''
+    with raises(
+        textx.exceptions.TextXError, match=r".*split requires a string parameter.*"
+    ):
+        metamodel_from_str(
+            """
             FQN[split]: ID('/'ID)*;
-        ''')
+        """
+        )
 
 
 def test_split_param_without_delimiter2():
-    with raises(textx.exceptions.TextXError,
-                match=r'.*split requires a non-empty string parameter.*'):
-        metamodel_from_str('''
+    with raises(
+        textx.exceptions.TextXError,
+        match=r".*split requires a non-empty string parameter.*",
+    ):
+        metamodel_from_str(
+            """
             FQN[split='']: ID('/'ID)*;
-        ''')
+        """
+        )
 
 
 def test_fully_qualified_name_ref_type_error():
@@ -211,9 +222,9 @@ def test_fully_qualified_name_ref_type_error():
     # MODEL PARSING
     #################################
 
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*p1.*'):
-        my_metamodel.model_from_str('''
+    with raises(textx.exceptions.TextXSemanticError, match=r".*p1.*"):
+        my_metamodel.model_from_str(
+            """
         package P1 {
             class Part1 {
             }
@@ -228,4 +239,5 @@ class C2 {
                 attr p1 p2b;
             }
         }
-        ''')
+        """
+        )
diff --git a/tests/functional/test_scoping/test_global_import_modules.py b/tests/functional/test_scoping/test_global_import_modules.py
index 0d1e9f31d..b83df1334 100644
--- a/tests/functional/test_scoping/test_global_import_modules.py
+++ b/tests/functional/test_scoping/test_global_import_modules.py
@@ -1,13 +1,13 @@
-from __future__ import unicode_literals
+from os.path import abspath, dirname, join
 
-from os.path import dirname, abspath, join
+from pytest import raises
 
 import textx.scoping.providers as scoping_providers
 from textx import metamodel_from_file
-from textx.scoping.tools import get_unique_named_object, \
-    check_unique_named_object_has_class
-
-from pytest import raises
+from textx.scoping.tools import (
+    check_unique_named_object_has_class,
+    get_unique_named_object,
+)
 
 
 def test_globalimports_basic_test_with_single_model_file():
@@ -21,23 +21,26 @@ def test_globalimports_basic_test_with_single_model_file():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'interface_model2',
-             'Interface.tx'))
+        join(abspath(dirname(__file__)), "interface_model2", "Interface.tx")
+    )
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.FQNGlobalRepo(
-            join(abspath(dirname(__file__)), "interface_model2",
-                 "model_a", "*.if"))})
+        {
+            "*.*": scoping_providers.FQNGlobalRepo(
+                join(abspath(dirname(__file__)), "interface_model2", "model_a", "*.if")
+            )
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)), "interface_model2",
-             "model_a", "all_in_one.if"))
+        join(abspath(dirname(__file__)), "interface_model2", "model_a", "all_in_one.if")
+    )
     my_model2 = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)), "interface_model2",
-             "model_a", "all_in_one.if"))
+        join(abspath(dirname(__file__)), "interface_model2", "model_a", "all_in_one.if")
+    )
 
     #################################
     # TEST MODEL
@@ -70,24 +73,27 @@ def test_globalimports_basic_test_with_single_model_file_and_global_repo():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'interface_model2',
-             'Interface.tx'),
-        global_repository=True)
+        join(abspath(dirname(__file__)), "interface_model2", "Interface.tx"),
+        global_repository=True,
+    )
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.FQNGlobalRepo(
-            join(abspath(dirname(__file__)), 'interface_model2',
-                 'model_a', '*.if'))})
+        {
+            "*.*": scoping_providers.FQNGlobalRepo(
+                join(abspath(dirname(__file__)), "interface_model2", "model_a", "*.if")
+            )
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)), "interface_model2",
-             "model_a", "all_in_one.if"))
+        join(abspath(dirname(__file__)), "interface_model2", "model_a", "all_in_one.if")
+    )
     my_model2 = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)), "interface_model2",
-             "model_a", "all_in_one.if"))
+        join(abspath(dirname(__file__)), "interface_model2", "model_a", "all_in_one.if")
+    )
 
     #################################
     # TEST MODEL
@@ -120,20 +126,23 @@ def test_globalimports_basic_test_with_distributed_model():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'interface_model2', 'Interface.tx'))
+        join(abspath(dirname(__file__)), "interface_model2", "Interface.tx")
+    )
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.FQNGlobalRepo(
-            join(abspath(dirname(__file__)),
-                 "interface_model2", "model_b", "*.if"))})
+        {
+            "*.*": scoping_providers.FQNGlobalRepo(
+                join(abspath(dirname(__file__)), "interface_model2", "model_b", "*.if")
+            )
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "interface_model2", "model_b", "app.if"))
+        join(abspath(dirname(__file__)), "interface_model2", "model_b", "app.if")
+    )
 
     #################################
     # TEST MODEL
@@ -141,8 +150,8 @@ def test_globalimports_basic_test_with_distributed_model():
 
     # check that "socket" is an interface
     inner_model = my_model._tx_model_repository.all_models[
-        join(abspath(dirname(__file__)),
-             "interface_model2", "model_b", "base.if")]
+        join(abspath(dirname(__file__)), "interface_model2", "model_b", "base.if")
+    ]
     check_unique_named_object_has_class(inner_model, "socket", "Interface")
 
     # check that "s.s1" is a reference to the socket interface
@@ -165,11 +174,11 @@ def test_globalimports_with_project_root_model_parameter():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'interface_model2',
-             'Interface.tx'))
+        join(abspath(dirname(__file__)), "interface_model2", "Interface.tx")
+    )
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.FQNGlobalRepo(
-            join("model_a", "*.if"))})
+        {"*.*": scoping_providers.FQNGlobalRepo(join("model_a", "*.if"))}
+    )
 
     #################################
     # MODEL PARSING
@@ -177,14 +186,15 @@ def test_globalimports_with_project_root_model_parameter():
 
     with raises(IOError):
         _ = my_meta_model.model_from_file(
-            join(abspath(dirname(__file__)), "interface_model2",
-                 "model_a", "all_in_one.if"))
+            join(
+                abspath(dirname(__file__)), "interface_model2", "model_a", "all_in_one.if"
+            )
+        )
 
     _ = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)), "interface_model2",
-             "model_a", "all_in_one.if"),
-        project_root=join(
-            abspath(dirname(__file__)), "interface_model2"))
+        join(abspath(dirname(__file__)), "interface_model2", "model_a", "all_in_one.if"),
+        project_root=join(abspath(dirname(__file__)), "interface_model2"),
+    )
 
     #################################
     # END
diff --git a/tests/functional/test_scoping/test_import_module.py b/tests/functional/test_scoping/test_import_module.py
index 6fe0afdcf..6db4b2dc0 100644
--- a/tests/functional/test_scoping/test_import_module.py
+++ b/tests/functional/test_scoping/test_import_module.py
@@ -1,15 +1,14 @@
-from __future__ import unicode_literals
-
-from os.path import dirname, abspath, join
+from os.path import abspath, dirname, join
 
 from pytest import raises
 
 import textx.exceptions
 import textx.scoping.providers as scoping_providers
-from textx import get_children, get_children_of_type
-from textx import metamodel_from_file
-from textx.scoping.tools import check_unique_named_object_has_class, \
-    get_unique_named_object
+from textx import get_children, get_children_of_type, metamodel_from_file
+from textx.scoping.tools import (
+    check_unique_named_object_has_class,
+    get_unique_named_object,
+)
 
 
 def test_model_without_imports():
@@ -21,18 +20,17 @@ def test_model_without_imports():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'interface_model1', 'Interface.tx'))
-    my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.FQNImportURI()})
+        join(abspath(dirname(__file__)), "interface_model1", "Interface.tx")
+    )
+    my_meta_model.register_scope_providers({"*.*": scoping_providers.FQNImportURI()})
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "interface_model1", "model_a", "all_in_one.if"))
+        join(abspath(dirname(__file__)), "interface_model1", "model_a", "all_in_one.if")
+    )
 
     #################################
     # TEST MODEL
@@ -60,21 +58,20 @@ def test_model_with_imports():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'interface_model1', 'Interface.tx'))
-    my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.FQNImportURI()})
+        join(abspath(dirname(__file__)), "interface_model1", "Interface.tx")
+    )
+    my_meta_model.register_scope_providers({"*.*": scoping_providers.FQNImportURI()})
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "interface_model1", "model_b", "app.if"))
+        join(abspath(dirname(__file__)), "interface_model1", "model_b", "app.if")
+    )
     my_model2 = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "interface_model1", "model_b", "app.if"))
+        join(abspath(dirname(__file__)), "interface_model1", "model_b", "app.if")
+    )
 
     #################################
     # TEST MODEL
@@ -82,8 +79,8 @@ def test_model_with_imports():
 
     # check that "socket" is an interface
     inner_model = my_model._tx_model_repository.all_models[
-        join(abspath(dirname(__file__)),
-             "interface_model1", "model_b", "base.if")]
+        join(abspath(dirname(__file__)), "interface_model1", "model_b", "base.if")
+    ]
     check_unique_named_object_has_class(inner_model, "socket", "Interface")
 
     # check that "s.s1" is a reference to the socket interface
@@ -111,25 +108,29 @@ def test_model_with_imports_and_errors():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'interface_model1', 'Interface.tx'))
-    my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.FQNImportURI()})
+        join(abspath(dirname(__file__)), "interface_model1", "Interface.tx")
+    )
+    my_meta_model.register_scope_providers({"*.*": scoping_providers.FQNImportURI()})
 
     #################################
     # MODEL PARSING
     #################################
 
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*Unknown object.*types.int.*'):
+    with raises(
+        textx.exceptions.TextXSemanticError, match=r".*Unknown object.*types.int.*"
+    ):
         my_meta_model.model_from_file(
-            join(abspath(dirname(__file__)),
-                 "interface_model1", "model_b", "app_error1.if"))
+            join(
+                abspath(dirname(__file__)), "interface_model1", "model_b", "app_error1.if"
+            )
+        )
 
-    with raises(IOError, match=r'.*file_not_found\.if.*'):
+    with raises(IOError, match=r".*file_not_found\.if.*"):
         my_meta_model.model_from_file(
-            join(abspath(dirname(__file__)),
-                 "interface_model1", "model_b", "app_error2.if"))
+            join(
+                abspath(dirname(__file__)), "interface_model1", "model_b", "app_error2.if"
+            )
+        )
 
     #################################
     # END
@@ -145,21 +146,21 @@ def test_model_with_imports_and_global_repo():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'interface_model1', 'Interface.tx'),
-        global_repository=True)
-    my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.FQNImportURI()})
+        join(abspath(dirname(__file__)), "interface_model1", "Interface.tx"),
+        global_repository=True,
+    )
+    my_meta_model.register_scope_providers({"*.*": scoping_providers.FQNImportURI()})
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "interface_model1", "model_b", "app.if"))
+        join(abspath(dirname(__file__)), "interface_model1", "model_b", "app.if")
+    )
     my_model2 = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "interface_model1", "model_b", "app.if"))
+        join(abspath(dirname(__file__)), "interface_model1", "model_b", "app.if")
+    )
 
     #################################
     # TEST MODEL
@@ -185,18 +186,17 @@ def test_model_with_circular_imports():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'interface_model1', 'Interface.tx'))
-    my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.FQNImportURI()})
+        join(abspath(dirname(__file__)), "interface_model1", "Interface.tx")
+    )
+    my_meta_model.register_scope_providers({"*.*": scoping_providers.FQNImportURI()})
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "interface_model1", "model_c", "A.if"))
+        join(abspath(dirname(__file__)), "interface_model1", "model_c", "A.if")
+    )
 
     #################################
     # TEST MODEL
@@ -205,28 +205,29 @@ def test_model_with_circular_imports():
     imports = get_children_of_type("Import", my_model)
     assert len(imports) > 0
     for i in imports:
-        assert 1 == len(i._tx_loaded_models)  # one file / load import
+        assert len(i._tx_loaded_models) == 1  # one file / load import
         assert i.importURI in i._tx_loaded_models[0]._tx_filename
 
     check_unique_named_object_has_class(my_model, "A", "Interface")
     a = get_unique_named_object(my_model, "A")
 
-    a_self = get_children(lambda x: hasattr(x, 'name') and x.name == "self", a)
+    a_self = get_children(lambda x: hasattr(x, "name") and x.name == "self", a)
     assert len(a_self) == 1
     a_self = a_self[0]
 
-    a_other = get_children(
-        lambda x: hasattr(x, 'name') and x.name == "other", a)
+    a_other = get_children(lambda x: hasattr(x, "name") and x.name == "other", a)
     assert len(a_other) == 1
     a_other = a_other[0]
 
     a_other_self = get_children(
-        lambda x: hasattr(x, 'name') and x.name == "self", a_other.ref)
+        lambda x: hasattr(x, "name") and x.name == "self", a_other.ref
+    )
     assert len(a_other_self) == 1
     a_other_self = a_other_self[0]
 
     a_other_other = get_children(
-        lambda x: hasattr(x, 'name') and x.name == "other", a_other.ref)
+        lambda x: hasattr(x, "name") and x.name == "other", a_other.ref
+    )
     assert len(a_other_other) == 1
     a_other_other = a_other_other[0]
 
@@ -249,25 +250,26 @@ def test_model_with_multi_import():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'interface_model1', 'Interface.tx'))
-    my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.FQNImportURI()})
+        join(abspath(dirname(__file__)), "interface_model1", "Interface.tx")
+    )
+    my_meta_model.register_scope_providers({"*.*": scoping_providers.FQNImportURI()})
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "interface_model1", "model_c", "A_multi_import.if"))
+        join(
+            abspath(dirname(__file__)), "interface_model1", "model_c", "A_multi_import.if"
+        )
+    )
 
     #################################
     # TEST MODEL
     #################################
 
     imports = get_children_of_type("Import", my_model)
-    assert 1 == len(imports)
+    assert len(imports) == 1
     i = imports[0]
-    assert 4 == len(i._tx_loaded_models)  # 4 files
-    assert 4 == len(set(i._tx_loaded_models))  # 4 different files
+    assert len(i._tx_loaded_models) == 4  # 4 files
+    assert len(set(i._tx_loaded_models)) == 4  # 4 different files
diff --git a/tests/functional/test_scoping/test_import_module_search_path_issue66.py b/tests/functional/test_scoping/test_import_module_search_path_issue66.py
index 4871fc8e0..acc477329 100644
--- a/tests/functional/test_scoping/test_import_module_search_path_issue66.py
+++ b/tests/functional/test_scoping/test_import_module_search_path_issue66.py
@@ -1,9 +1,9 @@
-from __future__ import unicode_literals
+from os.path import abspath, dirname, join
+
+from pytest import raises
 
-from os.path import dirname, abspath, join
 import textx.scoping.providers as scoping_providers
 from textx import metamodel_from_file
-from pytest import raises
 
 
 def test_model_with_imports_and_search_path_bad_case1():
@@ -15,17 +15,20 @@ def test_model_with_imports_and_search_path_bad_case1():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'issue66', 'task_specification.tx'))
+        join(abspath(dirname(__file__)), "issue66", "task_specification.tx")
+    )
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.PlainNameImportURI()})
+        {"*.*": scoping_providers.PlainNameImportURI()}
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
-    with raises(IOError, match=r'.*lib.*tasks.*'):
+    with raises(IOError, match=r".*lib.*tasks.*"):
         my_meta_model.model_from_file(
-            join(abspath(dirname(__file__)), "issue66", "assembly_car1.prog"))
+            join(abspath(dirname(__file__)), "issue66", "assembly_car1.prog")
+        )
 
     #################################
     # END
@@ -41,20 +44,23 @@ def test_model_with_imports_and_search_path_good_case():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'issue66', 'task_specification.tx'))
+        join(abspath(dirname(__file__)), "issue66", "task_specification.tx")
+    )
     search_path = [
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere1'),
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere2')
+        join(abspath(dirname(__file__)), "issue66", "somewhere1"),
+        join(abspath(dirname(__file__)), "issue66", "somewhere2"),
     ]
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.PlainNameImportURI(search_path=search_path)})
+        {"*.*": scoping_providers.PlainNameImportURI(search_path=search_path)}
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
     my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)), "issue66", "assembly_car1.prog"))
+        join(abspath(dirname(__file__)), "issue66", "assembly_car1.prog")
+    )
 
     #################################
     # END
@@ -70,21 +76,24 @@ def test_model_with_imports_and_search_path_bad_case2a():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'issue66', 'task_specification.tx'))
+        join(abspath(dirname(__file__)), "issue66", "task_specification.tx")
+    )
     search_path = [
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere1'),  # assembly
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere2xx')  # position
+        join(abspath(dirname(__file__)), "issue66", "somewhere1"),  # assembly
+        join(abspath(dirname(__file__)), "issue66", "somewhere2xx"),  # position
     ]
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.PlainNameImportURI(search_path=search_path)})
+        {"*.*": scoping_providers.PlainNameImportURI(search_path=search_path)}
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
-    with raises(IOError, match=r'.*position\.tasks.*'):
+    with raises(IOError, match=r".*position\.tasks.*"):
         my_meta_model.model_from_file(
-            join(abspath(dirname(__file__)), "issue66", "assembly_car1.prog"))
+            join(abspath(dirname(__file__)), "issue66", "assembly_car1.prog")
+        )
 
     #################################
     # END
@@ -100,24 +109,24 @@ def test_model_with_imports_and_search_path_bad_case2b():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'issue66',
-             'task_specification.tx'))
+        join(abspath(dirname(__file__)), "issue66", "task_specification.tx")
+    )
     search_path = [
-        join(abspath(dirname(__file__)), 'issue66',
-             'somewhere1xx'),  # assembly
-        join(abspath(dirname(__file__)), 'issue66',
-             'somewhere2')  # position
+        join(abspath(dirname(__file__)), "issue66", "somewhere1xx"),  # assembly
+        join(abspath(dirname(__file__)), "issue66", "somewhere2"),  # position
     ]
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.PlainNameImportURI(search_path=search_path)})
+        {"*.*": scoping_providers.PlainNameImportURI(search_path=search_path)}
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
-    with raises(IOError, match=r'.*assembly\.tasks.*'):
+    with raises(IOError, match=r".*assembly\.tasks.*"):
         my_meta_model.model_from_file(
-            join(abspath(dirname(__file__)), "issue66", "assembly_car1.prog"))
+            join(abspath(dirname(__file__)), "issue66", "assembly_car1.prog")
+        )
 
     #################################
     # END
@@ -133,16 +142,20 @@ def test_model_with_imports_and_search_path_bad_case_search_and_glob1():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'issue66', 'task_specification.tx'))
+        join(abspath(dirname(__file__)), "issue66", "task_specification.tx")
+    )
     search_path = [
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere1'),  # assembly
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere2')  # position
+        join(abspath(dirname(__file__)), "issue66", "somewhere1"),  # assembly
+        join(abspath(dirname(__file__)), "issue66", "somewhere2"),  # position
     ]
-    with raises(Exception,
-                match=r'you cannot use globbing together with a search path'):
+    with raises(Exception, match=r"you cannot use globbing together with a search path"):
         my_meta_model.register_scope_providers(
-            {"*.*": scoping_providers.PlainNameImportURI(
-                glob_args={}, search_path=search_path)})
+            {
+                "*.*": scoping_providers.PlainNameImportURI(
+                    glob_args={}, search_path=search_path
+                )
+            }
+        )
 
     #################################
     # END
@@ -159,21 +172,24 @@ def test_model_with_imports_and_search_path_bad_case_search_and_glob2():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'issue66', 'task_specification.tx'))
+        join(abspath(dirname(__file__)), "issue66", "task_specification.tx")
+    )
     search_path = [
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere1'),  # assembly
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere2')   # position
+        join(abspath(dirname(__file__)), "issue66", "somewhere1"),  # assembly
+        join(abspath(dirname(__file__)), "issue66", "somewhere2"),  # position
     ]
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.PlainNameImportURI(search_path=search_path)})
+        {"*.*": scoping_providers.PlainNameImportURI(search_path=search_path)}
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
-    with raises(IOError, match=r'.*assembly\.\*.*'):
+    with raises(IOError, match=r".*assembly\.\*.*"):
         my_meta_model.model_from_file(
-            join(abspath(dirname(__file__)), "issue66", "assembly_car2.prog"))
+            join(abspath(dirname(__file__)), "issue66", "assembly_car2.prog")
+        )
 
     #################################
     # END
@@ -190,13 +206,15 @@ def test_model_with_imports_relative_to_current_model():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'issue66', 'task_specification.tx'))
+        join(abspath(dirname(__file__)), "issue66", "task_specification.tx")
+    )
     search_path = [
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere1'),  # assembly
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere2')   # position
+        join(abspath(dirname(__file__)), "issue66", "somewhere1"),  # assembly
+        join(abspath(dirname(__file__)), "issue66", "somewhere2"),  # position
     ]
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.PlainNameImportURI(search_path=search_path)})
+        {"*.*": scoping_providers.PlainNameImportURI(search_path=search_path)}
+    )
 
     #################################
     # MODEL PARSING
@@ -207,15 +225,14 @@ def test_model_with_imports_relative_to_current_model():
     #   be preferred.
     # * on only exists locally.
     m = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             'issue66', 'local', 'assembly_car3.prog'))
+        join(abspath(dirname(__file__)), "issue66", "local", "assembly_car3.prog")
+    )
 
     # the model could be loaded --> the local path works (one file exists
     # only locally).
 
-    assert 2 == len(m.imports)
-    dirs = list(map(lambda i: dirname(i._tx_loaded_models[0]._tx_filename),
-                    m.imports))
+    assert len(m.imports) == 2
+    dirs = list(map(lambda i: dirname(i._tx_loaded_models[0]._tx_filename), m.imports))
     assert dirs[0] == dirs[1]
     # both included model have the same location. Thus, the local path is
     # preferred (as designed through the order of search paths).
diff --git a/tests/functional/test_scoping/test_inheritance.py b/tests/functional/test_scoping/test_inheritance.py
index 3bd052cd7..656a39c68 100644
--- a/tests/functional/test_scoping/test_inheritance.py
+++ b/tests/functional/test_scoping/test_inheritance.py
@@ -1,11 +1,8 @@
-from __future__ import unicode_literals
-
 import re
-from os.path import dirname, abspath, join
+from os.path import abspath, dirname, join
 
 import textx.scoping.providers as scoping_providers
-from textx import get_children_of_type
-from textx import metamodel_from_file
+from textx import get_children_of_type, metamodel_from_file
 
 
 def test_inheritance_processor():
@@ -17,27 +14,29 @@ def test_inheritance_processor():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'components_model1', 'Components.tx'))
-    my_meta_model.register_scope_providers({
-        "*.*": scoping_providers.FQN(),
-        "Connection.from_port":
-            scoping_providers.ExtRelativeName("from_inst.component",
-                                              "slots",
-                                              "extends"),
-        "Connection.to_port":
-            scoping_providers.ExtRelativeName("to_inst.component",
-                                              "slots",
-                                              "extends"),
-    })
+        join(abspath(dirname(__file__)), "components_model1", "Components.tx")
+    )
+    my_meta_model.register_scope_providers(
+        {
+            "*.*": scoping_providers.FQN(),
+            "Connection.from_port": scoping_providers.ExtRelativeName(
+                "from_inst.component", "slots", "extends"
+            ),
+            "Connection.to_port": scoping_providers.ExtRelativeName(
+                "to_inst.component", "slots", "extends"
+            ),
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "components_model1", "example_inherit3.components"))
+        join(
+            abspath(dirname(__file__)), "components_model1", "example_inherit3.components"
+        )
+    )
 
     #################################
     # TEST MODEL
@@ -59,8 +58,12 @@ def myformatter(compo):
         if len(compo.extends) == 0:
             return compo.name
         else:
-            return compo.name + "(" + ",".join(map(lambda x: myformatter(x),
-                                                   compo.extends)) + ")"
+            return (
+                compo.name
+                + "("
+                + ",".join(map(lambda x: myformatter(x), compo.extends))
+                + ")"
+            )
 
     res = "\n\n"
     # possibly in future version, the order need to be normalized...
@@ -68,7 +71,7 @@ def myformatter(compo):
         res += myformatter(c) + "\n"
 
     print(res)
-    assert re.sub(r'\s*', "", expected) == re.sub(r'\s*', "", res)
+    assert re.sub(r"\s*", "", expected) == re.sub(r"\s*", "", res)
 
     #################################
     # END
@@ -84,16 +87,18 @@ def test_inheritance_processor_rrel():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'components_model1', 'ComponentsRrel.tx'))
+        join(abspath(dirname(__file__)), "components_model1", "ComponentsRrel.tx")
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "components_model1", "example_inherit3.components"))
+        join(
+            abspath(dirname(__file__)), "components_model1", "example_inherit3.components"
+        )
+    )
 
     #################################
     # TEST MODEL
@@ -115,8 +120,12 @@ def myformatter(compo):
         if len(compo.extends) == 0:
             return compo.name
         else:
-            return compo.name + "(" + ",".join(map(lambda x: myformatter(x),
-                                                   compo.extends)) + ")"
+            return (
+                compo.name
+                + "("
+                + ",".join(map(lambda x: myformatter(x), compo.extends))
+                + ")"
+            )
 
     res = "\n\n"
     # possibly in future version, the order need to be normalized...
@@ -124,7 +133,7 @@ def myformatter(compo):
         res += myformatter(c) + "\n"
 
     print(res)
-    assert re.sub(r'\s*', "", expected) == re.sub(r'\s*', "", res)
+    assert re.sub(r"\s*", "", expected) == re.sub(r"\s*", "", res)
 
     #################################
     # END
diff --git a/tests/functional/test_scoping/test_issue170_combined_scope_provider.py b/tests/functional/test_scoping/test_issue170_combined_scope_provider.py
index 78e40a6ea..20be28327 100644
--- a/tests/functional/test_scoping/test_issue170_combined_scope_provider.py
+++ b/tests/functional/test_scoping/test_issue170_combined_scope_provider.py
@@ -1,18 +1,18 @@
-from __future__ import unicode_literals
 from textx import metamodel_from_str, textx_isinstance
 from textx.scoping.providers import FQN, RelativeName
 
 
-class MyScope(object):
+class MyScope:
     """
     Demo of a combined scope provider:
     Dependent of the underlying data structure an appropriate
     provider is selected...
     """
+
     def __init__(self, meta):
         self.meta = meta
-        self.pStart = RelativeName('parent.event.parameters')
-        self.pEvent1 = RelativeName('parent.parameters')
+        self.pStart = RelativeName("parent.event.parameters")
+        self.pEvent1 = RelativeName("parent.parameters")
         # also possible: self.pEvent1 = FQN()
 
     def __call__(self, obj, attr, obj_ref):
@@ -48,12 +48,9 @@ def test_combined_scope_provider():
      """
 
     meta = metamodel_from_str(grammar)
-    meta.register_scope_providers({
-        '*.*': FQN(),
-        'Repetition.paramName': MyScope(meta)
-    })
+    meta.register_scope_providers({"*.*": FQN(), "Repetition.paramName": MyScope(meta)})
 
-    code = '''
+    code = """
     event eventA (varA,varB,varC):
         x1=varA,
         x2=varB,
@@ -65,6 +62,6 @@ def test_combined_scope_provider():
         x2=varB,
         x3=varC )
     end
-    '''
+    """
 
     meta.model_from_str(code)
diff --git a/tests/functional/test_scoping/test_local_scope.py b/tests/functional/test_scoping/test_local_scope.py
index 87eb20ece..e4d550d28 100644
--- a/tests/functional/test_scoping/test_local_scope.py
+++ b/tests/functional/test_scoping/test_local_scope.py
@@ -1,14 +1,11 @@
-from __future__ import unicode_literals
-
-from os.path import dirname, abspath, join
+from os.path import abspath, dirname, join
 
 from pytest import raises
 
 import textx.exceptions
 import textx.scoping as scoping
 import textx.scoping.providers as scoping_providers
-from textx import get_children_of_type
-from textx import metamodel_from_file
+from textx import get_children_of_type, metamodel_from_file
 from textx.scoping.tools import get_unique_named_object
 
 
@@ -29,23 +26,26 @@ def to_port(obj, attr, obj_ref):
         return scoping.Postponed()
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'components_model1', 'Components.tx'))
-    my_meta_model.register_scope_providers({
-        "*.*": scoping_providers.FQN(),
-        "Connection.from_port": from_port,
-        "Connection.to_port": to_port
-    })
+        join(abspath(dirname(__file__)), "components_model1", "Components.tx")
+    )
+    my_meta_model.register_scope_providers(
+        {
+            "*.*": scoping_providers.FQN(),
+            "Connection.from_port": from_port,
+            "Connection.to_port": to_port,
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*Unresolvable cross references.*'):
+    with raises(
+        textx.exceptions.TextXSemanticError, match=r".*Unresolvable cross references.*"
+    ):
         my_meta_model.model_from_file(
-            join(abspath(dirname(__file__)),
-                 "components_model1", "example.components"))
+            join(abspath(dirname(__file__)), "components_model1", "example.components")
+        )
 
 
 def test_model_with_local_scope():
@@ -57,23 +57,27 @@ def test_model_with_local_scope():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'components_model1',
-             'Components.tx'))
-    my_meta_model.register_scope_providers({
-        "*.*": scoping_providers.FQN(),
-        "Connection.from_port":
-            scoping_providers.RelativeName("from_inst.component.slots"),
-        "Connection.to_port":
-            scoping_providers.RelativeName("to_inst.component.slots"),
-    })
+        join(abspath(dirname(__file__)), "components_model1", "Components.tx")
+    )
+    my_meta_model.register_scope_providers(
+        {
+            "*.*": scoping_providers.FQN(),
+            "Connection.from_port": scoping_providers.RelativeName(
+                "from_inst.component.slots"
+            ),
+            "Connection.to_port": scoping_providers.RelativeName(
+                "to_inst.component.slots"
+            ),
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)), "components_model1",
-             "example.components"))
+        join(abspath(dirname(__file__)), "components_model1", "example.components")
+    )
 
     #################################
     # TEST MODEL
@@ -83,9 +87,9 @@ def test_model_with_local_scope():
     action2 = get_unique_named_object(my_model, "action2")
     action3 = get_unique_named_object(my_model, "action3")
     connections = get_children_of_type("Connection", my_model)
-    selected_connections = list(filter(
-        lambda x: x.from_inst == action2 and x.to_inst == action3,
-        connections))
+    selected_connections = list(
+        filter(lambda x: x.from_inst == action2 and x.to_inst == action3, connections)
+    )
     assert len(selected_connections) == 1
 
     # test list of formats
@@ -111,25 +115,32 @@ def test_model_with_local_scope_and_error():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'components_model1',
-             'Components.tx'))
-    my_meta_model.register_scope_providers({
-        "*.*": scoping_providers.FQN(),
-        "Connection.from_port":
-            scoping_providers.RelativeName("from_inst.component.slots"),
-        "Connection.to_port":
-            scoping_providers.RelativeName("to_inst.component.slots")
-    })
+        join(abspath(dirname(__file__)), "components_model1", "Components.tx")
+    )
+    my_meta_model.register_scope_providers(
+        {
+            "*.*": scoping_providers.FQN(),
+            "Connection.from_port": scoping_providers.RelativeName(
+                "from_inst.component.slots"
+            ),
+            "Connection.to_port": scoping_providers.RelativeName(
+                "to_inst.component.slots"
+            ),
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*Unknown object.*input1.*SlotIn.*'):
+    with raises(
+        textx.exceptions.TextXSemanticError, match=r".*Unknown object.*input1.*SlotIn.*"
+    ):
         my_meta_model.model_from_file(
-            join(abspath(dirname(__file__)),
-                 "components_model1", "example_err1.components"))
+            join(
+                abspath(dirname(__file__)), "components_model1", "example_err1.components"
+            )
+        )
 
     #################################
     # END
@@ -145,25 +156,32 @@ def test_model_with_local_scope_and_error_267():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'components_model1',
-             'Components.tx'))
-    my_meta_model.register_scope_providers({
-        "*.*": scoping_providers.FQN(),
-        "Connection.from_port":
-            scoping_providers.RelativeName("from_inst.component.slots"),
-        "Connection.to_port":
-            scoping_providers.RelativeName("to_inst.component.slots")
-    })
+        join(abspath(dirname(__file__)), "components_model1", "Components.tx")
+    )
+    my_meta_model.register_scope_providers(
+        {
+            "*.*": scoping_providers.FQN(),
+            "Connection.from_port": scoping_providers.RelativeName(
+                "from_inst.component.slots"
+            ),
+            "Connection.to_port": scoping_providers.RelativeName(
+                "to_inst.component.slots"
+            ),
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*Unknown object.*pu.*SlotOut.*'):
+    with raises(
+        textx.exceptions.TextXSemanticError, match=r".*Unknown object.*pu.*SlotOut.*"
+    ):
         my_meta_model.model_from_file(
-            join(abspath(dirname(__file__)),
-                 "components_model1", "example_err2.components"))
+            join(
+                abspath(dirname(__file__)), "components_model1", "example_err2.components"
+            )
+        )
 
     #################################
     # END
@@ -179,27 +197,29 @@ def test_model_with_local_scope_and_inheritance2():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'components_model1',
-             'Components.tx'))
-    my_meta_model.register_scope_providers({
-        "*.*": scoping_providers.FQN(),
-        "Connection.from_port":
-            scoping_providers.ExtRelativeName("from_inst.component",
-                                              "slots",
-                                              "extends"),
-        "Connection.to_port":
-            scoping_providers.ExtRelativeName("to_inst.component",
-                                              "slots",
-                                              "extends"),
-    })
+        join(abspath(dirname(__file__)), "components_model1", "Components.tx")
+    )
+    my_meta_model.register_scope_providers(
+        {
+            "*.*": scoping_providers.FQN(),
+            "Connection.from_port": scoping_providers.ExtRelativeName(
+                "from_inst.component", "slots", "extends"
+            ),
+            "Connection.to_port": scoping_providers.ExtRelativeName(
+                "to_inst.component", "slots", "extends"
+            ),
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "components_model1", "example_inherit1.components"))
+        join(
+            abspath(dirname(__file__)), "components_model1", "example_inherit1.components"
+        )
+    )
 
     #################################
     # TEST MODEL
@@ -211,24 +231,27 @@ def test_model_with_local_scope_and_inheritance2():
     action3 = get_unique_named_object(my_model, "action3")
     end = get_unique_named_object(my_model, "end")
     connections = get_children_of_type("Connection", my_model)
-    selected_connections_12 = list(filter(
-        lambda x: x.from_inst == action1 and x.to_inst == action2,
-        connections))
-    selected_connections_3e = list(filter(
-        lambda x: x.from_inst == action3 and x.to_inst == end,
-        connections))
+    selected_connections_12 = list(
+        filter(lambda x: x.from_inst == action1 and x.to_inst == action2, connections)
+    )
+    selected_connections_3e = list(
+        filter(lambda x: x.from_inst == action3 and x.to_inst == end, connections)
+    )
     assert len(selected_connections_12) == 1
     assert len(selected_connections_3e) == 1
-    assert selected_connections_12[0].to_port is selected_connections_3e[0].\
-        to_port  # output3 is same
+    assert (
+        selected_connections_12[0].to_port is selected_connections_3e[0].to_port
+    )  # output3 is same
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "components_model1", "example_inherit2.components"))
+        join(
+            abspath(dirname(__file__)), "components_model1", "example_inherit2.components"
+        )
+    )
 
     #################################
     # TEST MODEL
@@ -241,15 +264,17 @@ def test_model_with_local_scope_and_inheritance2():
     action3 = get_unique_named_object(my_model, "action3")
     end = get_unique_named_object(my_model, "end")
     connections = get_children_of_type("Connection", my_model)
-    selected_connections_12 = list(filter(
-        lambda x: x.from_inst == action1 and x.to_inst == action2,
-        connections))
-    selected_connections_3e = list(filter(
-        lambda x: x.from_inst == action3 and x.to_inst == end, connections))
+    selected_connections_12 = list(
+        filter(lambda x: x.from_inst == action1 and x.to_inst == action2, connections)
+    )
+    selected_connections_3e = list(
+        filter(lambda x: x.from_inst == action3 and x.to_inst == end, connections)
+    )
     assert len(selected_connections_12) == 1
     assert len(selected_connections_3e) == 1
-    assert selected_connections_12[0].to_port is selected_connections_3e[0].\
-        to_port  # output3 is same
+    assert (
+        selected_connections_12[0].to_port is selected_connections_3e[0].to_port
+    )  # output3 is same
 
     #################################
     # END
@@ -275,34 +300,42 @@ def test_model_with_local_scope_postponed():
 
     sp1 = scoping_providers.RelativeName("from_inst.component.slots")
     my_meta_model1 = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'components_model1', 'Components.tx'))
-    my_meta_model1.register_scope_providers({
-        "*.*": scoping_providers.FQN(),
-        "Connection.from_port": sp1,
-        "Connection.to_port":
-            scoping_providers.RelativeName("to_inst.component.slots")
-    })
+        join(abspath(dirname(__file__)), "components_model1", "Components.tx")
+    )
+    my_meta_model1.register_scope_providers(
+        {
+            "*.*": scoping_providers.FQN(),
+            "Connection.from_port": sp1,
+            "Connection.to_port": scoping_providers.RelativeName(
+                "to_inst.component.slots"
+            ),
+        }
+    )
 
     sp2 = scoping_providers.RelativeName("from_inst.component.slots")
     my_meta_model2 = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'components_model2',
-             'Components.tx'))
-    my_meta_model2.register_scope_providers({
-        "*.*": scoping_providers.FQN(),
-        "Connection.from_port": sp2,
-        "Connection.to_port":
-            scoping_providers.RelativeName("to_inst.component.slots")
-    })
+        join(abspath(dirname(__file__)), "components_model2", "Components.tx")
+    )
+    my_meta_model2.register_scope_providers(
+        {
+            "*.*": scoping_providers.FQN(),
+            "Connection.from_port": sp2,
+            "Connection.to_port": scoping_providers.RelativeName(
+                "to_inst.component.slots"
+            ),
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
-    my_meta_model1.model_from_file(join(abspath(dirname(__file__)),
-                                   "components_model1", "example.components"))
-    my_meta_model2.model_from_file(join(abspath(dirname(__file__)),
-                                   "components_model2", "example.components"))
+    my_meta_model1.model_from_file(
+        join(abspath(dirname(__file__)), "components_model1", "example.components")
+    )
+    my_meta_model2.model_from_file(
+        join(abspath(dirname(__file__)), "components_model2", "example.components")
+    )
 
     #################################
     # TEST MODEL
@@ -325,25 +358,32 @@ def test_model_with_local_scope_wrong_type():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'components_model1', 'Components.tx'))
-    my_meta_model.register_scope_providers({
-        "*.*": scoping_providers.FQN(),
-        "Connection.from_port":
-            scoping_providers.RelativeName("from_inst.component.slots"),
-        "Connection.to_port":
-            scoping_providers.RelativeName("to_inst.component.slots"),
-    })
+        join(abspath(dirname(__file__)), "components_model1", "Components.tx")
+    )
+    my_meta_model.register_scope_providers(
+        {
+            "*.*": scoping_providers.FQN(),
+            "Connection.from_port": scoping_providers.RelativeName(
+                "from_inst.component.slots"
+            ),
+            "Connection.to_port": scoping_providers.RelativeName(
+                "to_inst.component.slots"
+            ),
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*wrong_port.*'):
+    with raises(textx.exceptions.TextXSemanticError, match=r".*wrong_port.*"):
         my_meta_model.model_from_file(
-            join(abspath(dirname(__file__)),
-                 "components_model1", "example_wrong_type.components"))
+            join(
+                abspath(dirname(__file__)),
+                "components_model1",
+                "example_wrong_type.components",
+            )
+        )
 
 
 def test_model_with_local_scope_and_bad_model_path():
@@ -355,24 +395,28 @@ def test_model_with_local_scope_and_bad_model_path():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'components_model1', 'Components.tx'))
-    my_meta_model.register_scope_providers({
-        "*.*": scoping_providers.FQN(),
-        "Connection.from_port":
+        join(abspath(dirname(__file__)), "components_model1", "Components.tx")
+    )
+    my_meta_model.register_scope_providers(
+        {
+            "*.*": scoping_providers.FQN(),
+            "Connection.from_port":
             # error (component is not a list)
             scoping_providers.RelativeName("from_inst.component"),
-        "Connection.to_port":
-            scoping_providers.RelativeName("to_inst.component.slots"),
-    })
+            "Connection.to_port": scoping_providers.RelativeName(
+                "to_inst.component.slots"
+            ),
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
-    with raises(textx.exceptions.TextXError,
-                match=r'.*expected path to list in the model '
-                      + r'\(from_inst.component\).*'):
+    with raises(
+        textx.exceptions.TextXError,
+        match=r".*expected path to list in the model " + r"\(from_inst.component\).*",
+    ):
         my_meta_model.model_from_file(
-            join(abspath(dirname(__file__)),
-                 "components_model1", "example.components"))
+            join(abspath(dirname(__file__)), "components_model1", "example.components")
+        )
diff --git a/tests/functional/test_scoping/test_local_scope_circular.py b/tests/functional/test_scoping/test_local_scope_circular.py
index 1ccd782f3..731a20905 100644
--- a/tests/functional/test_scoping/test_local_scope_circular.py
+++ b/tests/functional/test_scoping/test_local_scope_circular.py
@@ -1,10 +1,7 @@
-from __future__ import unicode_literals
-
-from os.path import dirname, abspath, join
+from os.path import abspath, dirname, join
 
 import textx.scoping.providers as scoping_providers
-from textx import get_children_of_type
-from textx import metamodel_from_file
+from textx import get_children_of_type, metamodel_from_file
 from textx.scoping.tools import get_unique_named_object
 
 
@@ -17,30 +14,34 @@ def test_model_with_local_scope_and_circular_ref_via_two_models():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'components_model1', 'Components.tx'),
-        global_repository=True)
+        join(abspath(dirname(__file__)), "components_model1", "Components.tx"),
+        global_repository=True,
+    )
     global_scope = scoping_providers.FQNGlobalRepo(
-        join(abspath(dirname(__file__)),
-             "components_model1", "example_?.components"))
-    my_meta_model.register_scope_providers({
-        "*.*": global_scope,
-        "Connection.from_port":
-            scoping_providers.RelativeName("from_inst.component.slots"),
-        "Connection.to_port":
-            scoping_providers.RelativeName("to_inst.component.slots")
-    })
+        join(abspath(dirname(__file__)), "components_model1", "example_?.components")
+    )
+    my_meta_model.register_scope_providers(
+        {
+            "*.*": global_scope,
+            "Connection.from_port": scoping_providers.RelativeName(
+                "from_inst.component.slots"
+            ),
+            "Connection.to_port": scoping_providers.RelativeName(
+                "to_inst.component.slots"
+            ),
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model_a = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "components_model1", "example_A.components"))
+        join(abspath(dirname(__file__)), "components_model1", "example_A.components")
+    )
     my_model_b = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "components_model1", "example_B.components"))
+        join(abspath(dirname(__file__)), "components_model1", "example_B.components")
+    )
 
     a_my_a = get_unique_named_object(my_model_a, "mya")
     a_my_b = get_unique_named_object(my_model_a, "myb")
@@ -56,12 +57,12 @@ def test_model_with_local_scope_and_circular_ref_via_two_models():
     a_connections = get_children_of_type("Connection", my_model_a)
     b_connections = get_children_of_type("Connection", my_model_b)
 
-    a_connection = list(filter(
-        lambda x: x.from_inst == a_my_a and x.to_inst == a_my_b,
-        a_connections))
-    b_connection = list(filter(
-        lambda x: x.from_inst == b_my_a and x.to_inst == b_my_b,
-        b_connections))
+    a_connection = list(
+        filter(lambda x: x.from_inst == a_my_a and x.to_inst == a_my_b, a_connections)
+    )
+    b_connection = list(
+        filter(lambda x: x.from_inst == b_my_a and x.to_inst == b_my_b, b_connections)
+    )
     assert len(a_connection) == 1
     assert len(b_connection) == 1
 
diff --git a/tests/functional/test_scoping/test_metamodel_provider.py b/tests/functional/test_scoping/test_metamodel_provider.py
index 2dec12b09..dade0ef53 100644
--- a/tests/functional/test_scoping/test_metamodel_provider.py
+++ b/tests/functional/test_scoping/test_metamodel_provider.py
@@ -1,13 +1,10 @@
-from __future__ import unicode_literals
-
-from os.path import dirname, abspath, join
+from os.path import abspath, dirname, join
 
 from pytest import raises
 
 import textx.scoping.providers as scoping_providers
-from textx import metamodel_from_file
+from textx import clear_language_registrations, metamodel_from_file, register_language
 from textx.scoping.tools import get_unique_named_object_in_all_models
-from textx import register_language, clear_language_registrations
 
 
 def test_metamodel_provider_basic_test():
@@ -27,41 +24,48 @@ def test_metamodel_provider_basic_test():
     #################################
 
     mm_components = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'metamodel_provider', 'Components.tx'))
-    mm_components.register_scope_providers({
-        "*.*": scoping_providers.FQNImportURI(),
-        "Connection.from_port":
-            scoping_providers.RelativeName("from_inst.component.slots"),
-        "Connection.to_port":
-            scoping_providers.RelativeName("to_inst.component.slots"),
-    })
+        join(abspath(dirname(__file__)), "metamodel_provider", "Components.tx")
+    )
+    mm_components.register_scope_providers(
+        {
+            "*.*": scoping_providers.FQNImportURI(),
+            "Connection.from_port": scoping_providers.RelativeName(
+                "from_inst.component.slots"
+            ),
+            "Connection.to_port": scoping_providers.RelativeName(
+                "to_inst.component.slots"
+            ),
+        }
+    )
 
     mm_users = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'metamodel_provider', 'Users.tx'))
-    mm_users.register_scope_providers({
-        "*.*": scoping_providers.FQNImportURI(),
-    })
+        join(abspath(dirname(__file__)), "metamodel_provider", "Users.tx")
+    )
+    mm_users.register_scope_providers(
+        {
+            "*.*": scoping_providers.FQNImportURI(),
+        }
+    )
 
     clear_language_registrations()
     register_language(
-        'components-dsl',
-        pattern='*.components',
-        description='demo',
-        metamodel=mm_components  # or a factory
+        "components-dsl",
+        pattern="*.components",
+        description="demo",
+        metamodel=mm_components,  # or a factory
     )
     register_language(
-        'users-dsl',
-        pattern='*.users',
-        description='demo',
-        metamodel=mm_users  # or a factory
+        "users-dsl",
+        pattern="*.users",
+        description="demo",
+        metamodel=mm_users,  # or a factory
     )
-    with raises(Exception, match=r'.*already registered.*'):
+    with raises(Exception, match=r".*already registered.*"):
         register_language(
-            'users-dsl',
-            pattern='*.users',
-            description='demo',
-            metamodel=mm_users  # or a factory
+            "users-dsl",
+            pattern="*.users",
+            description="demo",
+            metamodel=mm_users,  # or a factory
         )
 
     #################################
@@ -69,8 +73,8 @@ def test_metamodel_provider_basic_test():
     #################################
 
     my_model = mm_users.model_from_file(
-        join(abspath(dirname(__file__)),
-             "metamodel_provider", "example.users"))
+        join(abspath(dirname(__file__)), "metamodel_provider", "example.users")
+    )
 
     #################################
     # TEST MODEL
diff --git a/tests/functional/test_scoping/test_metamodel_provider2.py b/tests/functional/test_scoping/test_metamodel_provider2.py
index 788cdd65f..cf10207f2 100644
--- a/tests/functional/test_scoping/test_metamodel_provider2.py
+++ b/tests/functional/test_scoping/test_metamodel_provider2.py
@@ -1,10 +1,12 @@
-from __future__ import unicode_literals
-
-from os.path import dirname, abspath, join
+from os.path import abspath, dirname, join
 
 import textx.scoping.providers as scoping_providers
-from textx import metamodel_from_file, get_children_of_type
-from textx import register_language, clear_language_registrations
+from textx import (
+    clear_language_registrations,
+    get_children_of_type,
+    metamodel_from_file,
+    register_language,
+)
 
 
 def test_metamodel_provider_advanced_test():
@@ -22,39 +24,40 @@ def test_metamodel_provider_advanced_test():
     this_folder = dirname(abspath(__file__))
 
     def get_meta_model(global_repo, grammar_file_name):
-        mm = metamodel_from_file(join(this_folder, grammar_file_name),
-                                 debug=False)
-        mm.register_scope_providers({
-            "*.*": global_repo,
-            "Ingredient.unit": scoping_providers.ExtRelativeName("type",
-                                                                 "units",
-                                                                 "extends")
-        })
+        mm = metamodel_from_file(join(this_folder, grammar_file_name), debug=False)
+        mm.register_scope_providers(
+            {
+                "*.*": global_repo,
+                "Ingredient.unit": scoping_providers.ExtRelativeName(
+                    "type", "units", "extends"
+                ),
+            }
+        )
         return mm
 
     global_repo = scoping_providers.PlainNameGlobalRepo()
-    global_repo.register_models(
-        join(this_folder, "metamodel_provider2", "*.recipe"))
-    global_repo.register_models(
-        join(this_folder, "metamodel_provider2", "*.ingredient"))
+    global_repo.register_models(join(this_folder, "metamodel_provider2", "*.recipe"))
+    global_repo.register_models(join(this_folder, "metamodel_provider2", "*.ingredient"))
 
     i_mm = get_meta_model(
-        global_repo, join(this_folder, "metamodel_provider2", "Ingredient.tx"))
+        global_repo, join(this_folder, "metamodel_provider2", "Ingredient.tx")
+    )
     r_mm = get_meta_model(
-        global_repo, join(this_folder, "metamodel_provider2", "Recipe.tx"))
+        global_repo, join(this_folder, "metamodel_provider2", "Recipe.tx")
+    )
 
     clear_language_registrations()
     register_language(
-        'recipe-dsl',
-        pattern='*.recipe',
-        description='demo',
-        metamodel=r_mm  # or a factory
+        "recipe-dsl",
+        pattern="*.recipe",
+        description="demo",
+        metamodel=r_mm,  # or a factory
     )
     register_language(
-        'ingredient-dsl',
-        pattern='*.ingredient',
-        description='demo',
-        metamodel=i_mm  # or a factory
+        "ingredient-dsl",
+        pattern="*.ingredient",
+        description="demo",
+        metamodel=i_mm,  # or a factory
     )
 
     #################################
diff --git a/tests/functional/test_scoping/test_metamodel_provider3.py b/tests/functional/test_scoping/test_metamodel_provider3.py
index a744e2599..878a0b2e9 100644
--- a/tests/functional/test_scoping/test_metamodel_provider3.py
+++ b/tests/functional/test_scoping/test_metamodel_provider3.py
@@ -1,11 +1,12 @@
-from __future__ import unicode_literals
-
-from os.path import dirname, abspath, join
+from os.path import abspath, dirname, join
 
 import textx.scoping.providers as scoping_providers
-from textx import get_children_of_type
-from textx import (metamodel_from_file, register_language,
-                   clear_language_registrations)
+from textx import (
+    clear_language_registrations,
+    get_children_of_type,
+    metamodel_from_file,
+    register_language,
+)
 
 
 def test_metamodel_provider_advanced_test3_global():
@@ -23,47 +24,39 @@ def test_metamodel_provider_advanced_test3_global():
     this_folder = dirname(abspath(__file__))
 
     def get_meta_model(global_repo, grammar_file_name):
-        mm = metamodel_from_file(join(this_folder, grammar_file_name),
-                                 debug=False)
-        mm.register_scope_providers({
-            "*.*": global_repo,
-        })
+        mm = metamodel_from_file(join(this_folder, grammar_file_name), debug=False)
+        mm.register_scope_providers(
+            {
+                "*.*": global_repo,
+            }
+        )
         return mm
 
     global_repo_provider = scoping_providers.PlainNameGlobalRepo()
     global_repo_provider.register_models(
-        join(this_folder, "metamodel_provider3", "circular", "*.a"))
+        join(this_folder, "metamodel_provider3", "circular", "*.a")
+    )
     global_repo_provider.register_models(
-        join(this_folder, "metamodel_provider3", "circular", "*.b"))
+        join(this_folder, "metamodel_provider3", "circular", "*.b")
+    )
     global_repo_provider.register_models(
-        join(this_folder, "metamodel_provider3", "circular", "*.c"))
+        join(this_folder, "metamodel_provider3", "circular", "*.c")
+    )
 
     a_mm = get_meta_model(
-        global_repo_provider, join(this_folder,
-                                   "metamodel_provider3", "A.tx"))
+        global_repo_provider, join(this_folder, "metamodel_provider3", "A.tx")
+    )
     b_mm = get_meta_model(
-        global_repo_provider, join(this_folder,
-                                   "metamodel_provider3", "B.tx"))
+        global_repo_provider, join(this_folder, "metamodel_provider3", "B.tx")
+    )
     c_mm = get_meta_model(
-        global_repo_provider, join(this_folder,
-                                   "metamodel_provider3", "C.tx"))
+        global_repo_provider, join(this_folder, "metamodel_provider3", "C.tx")
+    )
 
     clear_language_registrations()
-    register_language(
-        'a-dsl',
-        pattern='*.a',
-        description='Test Lang A',
-        metamodel=a_mm)
-    register_language(
-        'b-dsl',
-        pattern='*.b',
-        description='Test Lang B',
-        metamodel=b_mm)
-    register_language(
-        'c-dsl',
-        pattern='*.c',
-        description='Test Lang C',
-        metamodel=c_mm)
+    register_language("a-dsl", pattern="*.a", description="Test Lang A", metamodel=a_mm)
+    register_language("b-dsl", pattern="*.b", description="Test Lang B", metamodel=b_mm)
+    register_language("c-dsl", pattern="*.c", description="Test Lang C", metamodel=c_mm)
 
     #################################
     # MODEL PARSING
@@ -94,6 +87,7 @@ def get_all(model_repo, what):
 
     # more checks
     from textx import textx_isinstance
+
     for a in lst:
         assert textx_isinstance(a, a_mm["Obj"])
         assert textx_isinstance(a, b_mm["Obj"])
@@ -120,48 +114,38 @@ def test_metamodel_provider_advanced_test3_import():
     this_folder = dirname(abspath(__file__))
 
     def get_meta_model(provider, grammar_file_name):
-        mm = metamodel_from_file(join(this_folder, grammar_file_name),
-                                 debug=False)
-        mm.register_scope_providers({
-            "*.*": provider,
-        })
+        mm = metamodel_from_file(join(this_folder, grammar_file_name), debug=False)
+        mm.register_scope_providers(
+            {
+                "*.*": provider,
+            }
+        )
         return mm
 
     import_lookup_provider = scoping_providers.PlainNameImportURI()
 
     a_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "A.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "A.tx")
+    )
     b_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "B.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "B.tx")
+    )
     c_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "C.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "C.tx")
+    )
 
     clear_language_registrations()
-    register_language(
-        'a-dsl',
-        pattern='*.a',
-        description='Test Lang A',
-        metamodel=a_mm)
-    register_language(
-        'b-dsl',
-        pattern='*.b',
-        description='Test Lang B',
-        metamodel=b_mm)
-    register_language(
-        'c-dsl',
-        pattern='*.c',
-        description='Test Lang C',
-        metamodel=c_mm)
+    register_language("a-dsl", pattern="*.a", description="Test Lang A", metamodel=a_mm)
+    register_language("b-dsl", pattern="*.b", description="Test Lang B", metamodel=b_mm)
+    register_language("c-dsl", pattern="*.c", description="Test Lang C", metamodel=c_mm)
 
     #################################
     # MODEL PARSING
     #################################
 
     m = a_mm.model_from_file(
-        join(this_folder, "metamodel_provider3", "circular", "model_a.a"))
+        join(this_folder, "metamodel_provider3", "circular", "model_a.a")
+    )
     model_repo = m._tx_model_repository.all_models
 
     #################################
@@ -206,52 +190,41 @@ def test_metamodel_provider_advanced_test3_inheritance():
     this_folder = dirname(abspath(__file__))
 
     def get_meta_model(provider, grammar_file_name):
-        mm = metamodel_from_file(join(this_folder, grammar_file_name),
-                                 debug=False)
-        mm.register_scope_providers({
-            "*.*": provider,
-            "Call.method": scoping_providers.ExtRelativeName("obj.ref",
-                                                             "methods",
-                                                             "extends")
-        })
+        mm = metamodel_from_file(join(this_folder, grammar_file_name), debug=False)
+        mm.register_scope_providers(
+            {
+                "*.*": provider,
+                "Call.method": scoping_providers.ExtRelativeName(
+                    "obj.ref", "methods", "extends"
+                ),
+            }
+        )
         return mm
 
     import_lookup_provider = scoping_providers.FQNImportURI()
 
     a_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "A.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "A.tx")
+    )
     b_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "B.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "B.tx")
+    )
     c_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "C.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "C.tx")
+    )
 
     clear_language_registrations()
-    register_language(
-        'a-dsl',
-        pattern='*.a',
-        description='Test Lang A',
-        metamodel=a_mm)
-    register_language(
-        'b-dsl',
-        pattern='*.b',
-        description='Test Lang B',
-        metamodel=b_mm)
-    register_language(
-        'c-dsl',
-        pattern='*.c',
-        description='Test Lang C',
-        metamodel=c_mm)
+    register_language("a-dsl", pattern="*.a", description="Test Lang A", metamodel=a_mm)
+    register_language("b-dsl", pattern="*.b", description="Test Lang B", metamodel=b_mm)
+    register_language("c-dsl", pattern="*.c", description="Test Lang C", metamodel=c_mm)
 
     #################################
     # MODEL PARSING
     #################################
 
     m = a_mm.model_from_file(
-        join(this_folder, "metamodel_provider3",
-             "inheritance", "model_a.a"))
+        join(this_folder, "metamodel_provider3", "inheritance", "model_a.a")
+    )
     model_repo = m._tx_model_repository.all_models
 
     #################################
@@ -289,52 +262,41 @@ def test_metamodel_provider_advanced_test3_inheritance2():
     this_folder = dirname(abspath(__file__))
 
     def get_meta_model(provider, grammar_file_name):
-        mm = metamodel_from_file(join(this_folder, grammar_file_name),
-                                 debug=False)
-        mm.register_scope_providers({
-            "*.*": provider,
-            "Call.method": scoping_providers.ExtRelativeName("obj.ref",
-                                                             "methods",
-                                                             "extends")
-        })
+        mm = metamodel_from_file(join(this_folder, grammar_file_name), debug=False)
+        mm.register_scope_providers(
+            {
+                "*.*": provider,
+                "Call.method": scoping_providers.ExtRelativeName(
+                    "obj.ref", "methods", "extends"
+                ),
+            }
+        )
         return mm
 
     import_lookup_provider = scoping_providers.FQNImportURI()
 
     a_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "A.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "A.tx")
+    )
     b_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "B.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "B.tx")
+    )
     c_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "C.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "C.tx")
+    )
 
     clear_language_registrations()
-    register_language(
-        'a-dsl',
-        pattern='*.a',
-        description='Test Lang A',
-        metamodel=a_mm)
-    register_language(
-        'b-dsl',
-        pattern='*.b',
-        description='Test Lang B',
-        metamodel=b_mm)
-    register_language(
-        'c-dsl',
-        pattern='*.c',
-        description='Test Lang C',
-        metamodel=c_mm)
+    register_language("a-dsl", pattern="*.a", description="Test Lang A", metamodel=a_mm)
+    register_language("b-dsl", pattern="*.b", description="Test Lang B", metamodel=b_mm)
+    register_language("c-dsl", pattern="*.c", description="Test Lang C", metamodel=c_mm)
 
     #################################
     # MODEL PARSING
     #################################
 
     m = a_mm.model_from_file(
-        join(this_folder, "metamodel_provider3",
-             "inheritance2", "model_a.a"))
+        join(this_folder, "metamodel_provider3", "inheritance2", "model_a.a")
+    )
     model_repo = m._tx_model_repository.all_models
 
     #################################
@@ -357,10 +319,9 @@ def get_all(model_repo, what):
         assert a.method
 
     # check that all models have different parsers
-    parsers = list(
-        map(lambda x: x._tx_parser, model_repo))
-    assert 4 == len(parsers)  # 4 files -> 4 parsers
-    assert 4 == len(set(parsers))  # 4 different parsers
+    parsers = list(map(lambda x: x._tx_parser, model_repo))
+    assert len(parsers) == 4  # 4 files -> 4 parsers
+    assert len(set(parsers)) == 4  # 4 different parsers
 
     #################################
     # END
@@ -380,52 +341,41 @@ def test_metamodel_provider_advanced_test3_diamond():
     this_folder = dirname(abspath(__file__))
 
     def get_meta_model(provider, grammar_file_name):
-        mm = metamodel_from_file(join(this_folder, grammar_file_name),
-                                 debug=False)
-        mm.register_scope_providers({
-            "*.*": provider,
-            "Call.method": scoping_providers.ExtRelativeName("obj.ref",
-                                                             "methods",
-                                                             "extends")
-        })
+        mm = metamodel_from_file(join(this_folder, grammar_file_name), debug=False)
+        mm.register_scope_providers(
+            {
+                "*.*": provider,
+                "Call.method": scoping_providers.ExtRelativeName(
+                    "obj.ref", "methods", "extends"
+                ),
+            }
+        )
         return mm
 
     import_lookup_provider = scoping_providers.FQNImportURI()
 
     a_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "A.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "A.tx")
+    )
     b_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "B.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "B.tx")
+    )
     c_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "C.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "C.tx")
+    )
 
     clear_language_registrations()
-    register_language(
-        'a-dsl',
-        pattern='*.a',
-        description='Test Lang A',
-        metamodel=a_mm)
-    register_language(
-        'b-dsl',
-        pattern='*.b',
-        description='Test Lang B',
-        metamodel=b_mm)
-    register_language(
-        'c-dsl',
-        pattern='*.c',
-        description='Test Lang C',
-        metamodel=c_mm)
+    register_language("a-dsl", pattern="*.a", description="Test Lang A", metamodel=a_mm)
+    register_language("b-dsl", pattern="*.b", description="Test Lang B", metamodel=b_mm)
+    register_language("c-dsl", pattern="*.c", description="Test Lang C", metamodel=c_mm)
 
     #################################
     # MODEL PARSING
     #################################
 
     m = a_mm.model_from_file(
-        join(this_folder, "metamodel_provider3",
-             "diamond", "A_includes_B_C.a"))
+        join(this_folder, "metamodel_provider3", "diamond", "A_includes_B_C.a")
+    )
     model_repo = m._tx_model_repository.all_models
 
     #################################
@@ -449,8 +399,8 @@ def get_all(model_repo, what):
 
     # check that all models have different parsers
     parsers = list(map(lambda x: x._tx_parser, model_repo))
-    assert 4 == len(parsers)  # 4 files -> 4 parsers
-    assert 4 == len(set(parsers))  # 4 different parsers
+    assert len(parsers) == 4  # 4 files -> 4 parsers
+    assert len(set(parsers)) == 4  # 4 different parsers
 
     #################################
     # END
diff --git a/tests/functional/test_scoping/test_metamodel_provider3_custom_classes.py b/tests/functional/test_scoping/test_metamodel_provider3_custom_classes.py
index bf46161f8..132fb65dc 100644
--- a/tests/functional/test_scoping/test_metamodel_provider3_custom_classes.py
+++ b/tests/functional/test_scoping/test_metamodel_provider3_custom_classes.py
@@ -1,16 +1,18 @@
-from __future__ import unicode_literals
+from os.path import abspath, dirname, join
 
-from os.path import dirname, abspath, join
+import attr
 
 import textx.scoping.providers as scoping_providers
-from textx import get_children_of_type
-from textx import (metamodel_from_file, register_language,
-                   clear_language_registrations)
-import attr
+from textx import (
+    clear_language_registrations,
+    get_children_of_type,
+    metamodel_from_file,
+    register_language,
+)
 
 
 @attr.s(frozen=True)
-class Cls(object):
+class Cls:
     parent = attr.ib()
     name = attr.ib()
     extends = attr.ib()
@@ -18,7 +20,7 @@ class Cls(object):
 
 
 @attr.s(frozen=True)
-class Obj(object):
+class Obj:
     parent = attr.ib()
     name = attr.ib()
     ref = attr.ib()
@@ -39,47 +41,41 @@ def test_metamodel_provider_advanced_test3_global():
     this_folder = dirname(abspath(__file__))
 
     def get_meta_model(global_repo, grammar_file_name):
-        mm = metamodel_from_file(join(this_folder, grammar_file_name),
-                                 debug=False, classes=[Cls, Obj])
-        mm.register_scope_providers({
-            "*.*": global_repo,
-        })
+        mm = metamodel_from_file(
+            join(this_folder, grammar_file_name), debug=False, classes=[Cls, Obj]
+        )
+        mm.register_scope_providers(
+            {
+                "*.*": global_repo,
+            }
+        )
         return mm
 
     global_repo_provider = scoping_providers.PlainNameGlobalRepo()
     global_repo_provider.register_models(
-        join(this_folder, "metamodel_provider3", "circular", "*.a"))
+        join(this_folder, "metamodel_provider3", "circular", "*.a")
+    )
     global_repo_provider.register_models(
-        join(this_folder, "metamodel_provider3", "circular", "*.b"))
+        join(this_folder, "metamodel_provider3", "circular", "*.b")
+    )
     global_repo_provider.register_models(
-        join(this_folder, "metamodel_provider3", "circular", "*.c"))
+        join(this_folder, "metamodel_provider3", "circular", "*.c")
+    )
 
     a_mm = get_meta_model(
-        global_repo_provider, join(this_folder,
-                                   "metamodel_provider3", "A.tx"))
+        global_repo_provider, join(this_folder, "metamodel_provider3", "A.tx")
+    )
     b_mm = get_meta_model(
-        global_repo_provider, join(this_folder,
-                                   "metamodel_provider3", "B.tx"))
+        global_repo_provider, join(this_folder, "metamodel_provider3", "B.tx")
+    )
     c_mm = get_meta_model(
-        global_repo_provider, join(this_folder,
-                                   "metamodel_provider3", "C.tx"))
+        global_repo_provider, join(this_folder, "metamodel_provider3", "C.tx")
+    )
 
     clear_language_registrations()
-    register_language(
-        'a-dsl',
-        pattern='*.a',
-        description='Test Lang A',
-        metamodel=a_mm)
-    register_language(
-        'b-dsl',
-        pattern='*.b',
-        description='Test Lang B',
-        metamodel=b_mm)
-    register_language(
-        'c-dsl',
-        pattern='*.c',
-        description='Test Lang C',
-        metamodel=c_mm)
+    register_language("a-dsl", pattern="*.a", description="Test Lang A", metamodel=a_mm)
+    register_language("b-dsl", pattern="*.b", description="Test Lang B", metamodel=b_mm)
+    register_language("c-dsl", pattern="*.c", description="Test Lang C", metamodel=c_mm)
 
     #################################
     # MODEL PARSING
@@ -110,6 +106,7 @@ def get_all(model_repo, what):
 
     # more checks
     from textx import textx_isinstance
+
     for a in lst:
         assert textx_isinstance(a, a_mm["Obj"])
         assert textx_isinstance(a, b_mm["Obj"])
@@ -136,48 +133,40 @@ def test_metamodel_provider_advanced_test3_import():
     this_folder = dirname(abspath(__file__))
 
     def get_meta_model(provider, grammar_file_name):
-        mm = metamodel_from_file(join(this_folder, grammar_file_name),
-                                 debug=False, classes=[Cls, Obj])
-        mm.register_scope_providers({
-            "*.*": provider,
-        })
+        mm = metamodel_from_file(
+            join(this_folder, grammar_file_name), debug=False, classes=[Cls, Obj]
+        )
+        mm.register_scope_providers(
+            {
+                "*.*": provider,
+            }
+        )
         return mm
 
     import_lookup_provider = scoping_providers.PlainNameImportURI()
 
     a_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "A.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "A.tx")
+    )
     b_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "B.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "B.tx")
+    )
     c_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "C.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "C.tx")
+    )
 
     clear_language_registrations()
-    register_language(
-        'a-dsl',
-        pattern='*.a',
-        description='Test Lang A',
-        metamodel=a_mm)
-    register_language(
-        'b-dsl',
-        pattern='*.b',
-        description='Test Lang B',
-        metamodel=b_mm)
-    register_language(
-        'c-dsl',
-        pattern='*.c',
-        description='Test Lang C',
-        metamodel=c_mm)
+    register_language("a-dsl", pattern="*.a", description="Test Lang A", metamodel=a_mm)
+    register_language("b-dsl", pattern="*.b", description="Test Lang B", metamodel=b_mm)
+    register_language("c-dsl", pattern="*.c", description="Test Lang C", metamodel=c_mm)
 
     #################################
     # MODEL PARSING
     #################################
 
     m = a_mm.model_from_file(
-        join(this_folder, "metamodel_provider3", "circular", "model_a.a"))
+        join(this_folder, "metamodel_provider3", "circular", "model_a.a")
+    )
     model_repo = m._tx_model_repository.all_models
 
     #################################
@@ -222,52 +211,43 @@ def test_metamodel_provider_advanced_test3_inheritance():
     this_folder = dirname(abspath(__file__))
 
     def get_meta_model(provider, grammar_file_name):
-        mm = metamodel_from_file(join(this_folder, grammar_file_name),
-                                 debug=False, classes=[Cls, Obj])
-        mm.register_scope_providers({
-            "*.*": provider,
-            "Call.method": scoping_providers.ExtRelativeName("obj.ref",
-                                                             "methods",
-                                                             "extends")
-        })
+        mm = metamodel_from_file(
+            join(this_folder, grammar_file_name), debug=False, classes=[Cls, Obj]
+        )
+        mm.register_scope_providers(
+            {
+                "*.*": provider,
+                "Call.method": scoping_providers.ExtRelativeName(
+                    "obj.ref", "methods", "extends"
+                ),
+            }
+        )
         return mm
 
     import_lookup_provider = scoping_providers.FQNImportURI()
 
     a_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "A.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "A.tx")
+    )
     b_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "B.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "B.tx")
+    )
     c_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "C.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "C.tx")
+    )
 
     clear_language_registrations()
-    register_language(
-        'a-dsl',
-        pattern='*.a',
-        description='Test Lang A',
-        metamodel=a_mm)
-    register_language(
-        'b-dsl',
-        pattern='*.b',
-        description='Test Lang B',
-        metamodel=b_mm)
-    register_language(
-        'c-dsl',
-        pattern='*.c',
-        description='Test Lang C',
-        metamodel=c_mm)
+    register_language("a-dsl", pattern="*.a", description="Test Lang A", metamodel=a_mm)
+    register_language("b-dsl", pattern="*.b", description="Test Lang B", metamodel=b_mm)
+    register_language("c-dsl", pattern="*.c", description="Test Lang C", metamodel=c_mm)
 
     #################################
     # MODEL PARSING
     #################################
 
     m = a_mm.model_from_file(
-        join(this_folder, "metamodel_provider3",
-             "inheritance", "model_a.a"))
+        join(this_folder, "metamodel_provider3", "inheritance", "model_a.a")
+    )
     model_repo = m._tx_model_repository.all_models
 
     #################################
@@ -305,52 +285,43 @@ def test_metamodel_provider_advanced_test3_inheritance2():
     this_folder = dirname(abspath(__file__))
 
     def get_meta_model(provider, grammar_file_name):
-        mm = metamodel_from_file(join(this_folder, grammar_file_name),
-                                 debug=False, classes=[Cls, Obj])
-        mm.register_scope_providers({
-            "*.*": provider,
-            "Call.method": scoping_providers.ExtRelativeName("obj.ref",
-                                                             "methods",
-                                                             "extends")
-        })
+        mm = metamodel_from_file(
+            join(this_folder, grammar_file_name), debug=False, classes=[Cls, Obj]
+        )
+        mm.register_scope_providers(
+            {
+                "*.*": provider,
+                "Call.method": scoping_providers.ExtRelativeName(
+                    "obj.ref", "methods", "extends"
+                ),
+            }
+        )
         return mm
 
     import_lookup_provider = scoping_providers.FQNImportURI()
 
     a_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "A.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "A.tx")
+    )
     b_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "B.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "B.tx")
+    )
     c_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "C.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "C.tx")
+    )
 
     clear_language_registrations()
-    register_language(
-        'a-dsl',
-        pattern='*.a',
-        description='Test Lang A',
-        metamodel=a_mm)
-    register_language(
-        'b-dsl',
-        pattern='*.b',
-        description='Test Lang B',
-        metamodel=b_mm)
-    register_language(
-        'c-dsl',
-        pattern='*.c',
-        description='Test Lang C',
-        metamodel=c_mm)
+    register_language("a-dsl", pattern="*.a", description="Test Lang A", metamodel=a_mm)
+    register_language("b-dsl", pattern="*.b", description="Test Lang B", metamodel=b_mm)
+    register_language("c-dsl", pattern="*.c", description="Test Lang C", metamodel=c_mm)
 
     #################################
     # MODEL PARSING
     #################################
 
     m = a_mm.model_from_file(
-        join(this_folder, "metamodel_provider3",
-             "inheritance2", "model_a.a"))
+        join(this_folder, "metamodel_provider3", "inheritance2", "model_a.a")
+    )
     model_repo = m._tx_model_repository.all_models
 
     #################################
@@ -374,8 +345,8 @@ def get_all(model_repo, what):
 
     # check that all models have different parsers
     parsers = list(map(lambda x: x._tx_parser, model_repo))
-    assert 4 == len(parsers)  # 4 files -> 4 parsers
-    assert 4 == len(set(parsers))  # 4 different parsers
+    assert len(parsers) == 4  # 4 files -> 4 parsers
+    assert len(set(parsers)) == 4  # 4 different parsers
 
     #################################
     # END
@@ -395,52 +366,43 @@ def test_metamodel_provider_advanced_test3_diamond():
     this_folder = dirname(abspath(__file__))
 
     def get_meta_model(provider, grammar_file_name):
-        mm = metamodel_from_file(join(this_folder, grammar_file_name),
-                                 debug=False, classes=[Cls, Obj])
-        mm.register_scope_providers({
-            "*.*": provider,
-            "Call.method": scoping_providers.ExtRelativeName("obj.ref",
-                                                             "methods",
-                                                             "extends")
-        })
+        mm = metamodel_from_file(
+            join(this_folder, grammar_file_name), debug=False, classes=[Cls, Obj]
+        )
+        mm.register_scope_providers(
+            {
+                "*.*": provider,
+                "Call.method": scoping_providers.ExtRelativeName(
+                    "obj.ref", "methods", "extends"
+                ),
+            }
+        )
         return mm
 
     import_lookup_provider = scoping_providers.FQNImportURI()
 
     a_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "A.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "A.tx")
+    )
     b_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "B.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "B.tx")
+    )
     c_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "C.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "C.tx")
+    )
 
     clear_language_registrations()
-    register_language(
-        'a-dsl',
-        pattern='*.a',
-        description='Test Lang A',
-        metamodel=a_mm)
-    register_language(
-        'b-dsl',
-        pattern='*.b',
-        description='Test Lang B',
-        metamodel=b_mm)
-    register_language(
-        'c-dsl',
-        pattern='*.c',
-        description='Test Lang C',
-        metamodel=c_mm)
+    register_language("a-dsl", pattern="*.a", description="Test Lang A", metamodel=a_mm)
+    register_language("b-dsl", pattern="*.b", description="Test Lang B", metamodel=b_mm)
+    register_language("c-dsl", pattern="*.c", description="Test Lang C", metamodel=c_mm)
 
     #################################
     # MODEL PARSING
     #################################
 
     m = a_mm.model_from_file(
-        join(this_folder, "metamodel_provider3",
-             "diamond", "A_includes_B_C.a"))
+        join(this_folder, "metamodel_provider3", "diamond", "A_includes_B_C.a")
+    )
     model_repo = m._tx_model_repository.all_models
 
     #################################
@@ -464,8 +426,8 @@ def get_all(model_repo, what):
 
     # check that all models have different parsers
     parsers = list(map(lambda x: x._tx_parser, model_repo))
-    assert 4 == len(parsers)  # 4 files -> 4 parsers
-    assert 4 == len(set(parsers))  # 4 different parsers
+    assert len(parsers) == 4  # 4 files -> 4 parsers
+    assert len(set(parsers)) == 4  # 4 different parsers
 
     #################################
     # END
diff --git a/tests/functional/test_scoping/test_metamodel_provider_utf_16_le.py b/tests/functional/test_scoping/test_metamodel_provider_utf_16_le.py
index 7af30dbf7..1c7ddd2d1 100644
--- a/tests/functional/test_scoping/test_metamodel_provider_utf_16_le.py
+++ b/tests/functional/test_scoping/test_metamodel_provider_utf_16_le.py
@@ -1,11 +1,8 @@
-from __future__ import unicode_literals
-
-from os.path import dirname, abspath, join
+from os.path import abspath, dirname, join
 
 import textx.scoping.providers as scoping_providers
+from textx import clear_language_registrations, metamodel_from_file, register_language
 from textx.scoping.tools import get_unique_named_object_in_all_models
-from textx import (metamodel_from_file, register_language,
-                   clear_language_registrations)
 
 
 def test_metamodel_provider_utf_16_le_basic_test():
@@ -18,35 +15,41 @@ def test_metamodel_provider_utf_16_le_basic_test():
     #################################
 
     mm_components = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'metamodel_provider_utf-16-le', 'Components.tx'))
-    mm_components.register_scope_providers({
-        "*.*": scoping_providers.FQNImportURI(),
-        "Connection.from_port":
-            scoping_providers.RelativeName("from_inst.component.slots"),
-        "Connection.to_port":
-            scoping_providers.RelativeName("to_inst.component.slots"),
-    })
+        join(abspath(dirname(__file__)), "metamodel_provider_utf-16-le", "Components.tx")
+    )
+    mm_components.register_scope_providers(
+        {
+            "*.*": scoping_providers.FQNImportURI(),
+            "Connection.from_port": scoping_providers.RelativeName(
+                "from_inst.component.slots"
+            ),
+            "Connection.to_port": scoping_providers.RelativeName(
+                "to_inst.component.slots"
+            ),
+        }
+    )
 
     mm_users = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'metamodel_provider_utf-16-le', 'Users.tx'))
-    mm_users.register_scope_providers({
-        "*.*": scoping_providers.FQNImportURI(),
-    })
+        join(abspath(dirname(__file__)), "metamodel_provider_utf-16-le", "Users.tx")
+    )
+    mm_users.register_scope_providers(
+        {
+            "*.*": scoping_providers.FQNImportURI(),
+        }
+    )
 
     clear_language_registrations()
     register_language(
-        'components-dsl',
-        pattern='*.components',
-        description='demo',
-        metamodel=mm_components  # or a factory
+        "components-dsl",
+        pattern="*.components",
+        description="demo",
+        metamodel=mm_components,  # or a factory
     )
     register_language(
-        'users-dsl',
-        pattern='*.users',
-        description='demo',
-        metamodel=mm_users  # or a factory
+        "users-dsl",
+        pattern="*.users",
+        description="demo",
+        metamodel=mm_users,  # or a factory
     )
 
     #################################
@@ -54,9 +57,9 @@ def test_metamodel_provider_utf_16_le_basic_test():
     #################################
 
     my_model = mm_users.model_from_file(
-        join(abspath(dirname(__file__)),
-             "metamodel_provider_utf-16-le", "example.users"),
-        encoding='utf-16-le')
+        join(abspath(dirname(__file__)), "metamodel_provider_utf-16-le", "example.users"),
+        encoding="utf-16-le",
+    )
 
     #################################
     # TEST MODEL
diff --git a/tests/functional/test_scoping/test_model_export.py b/tests/functional/test_scoping/test_model_export.py
index 9697b3fd0..172e4377f 100644
--- a/tests/functional/test_scoping/test_model_export.py
+++ b/tests/functional/test_scoping/test_model_export.py
@@ -1,12 +1,9 @@
-from __future__ import unicode_literals
-
 import io
-from os.path import dirname, abspath, join, sep
+from os.path import abspath, dirname, join, sep
 
 import textx.export as export
 import textx.scoping.providers as scoping_providers
-from textx import (metamodel_from_file, register_language,
-                   clear_language_registrations)
+from textx import clear_language_registrations, metamodel_from_file, register_language
 
 
 def test_model_export():
@@ -23,52 +20,41 @@ def test_model_export():
     this_folder = dirname(abspath(__file__))
 
     def get_meta_model(provider, grammar_file_name):
-        mm = metamodel_from_file(join(this_folder, grammar_file_name),
-                                 debug=False)
-        mm.register_scope_providers({
-            "*.*": provider,
-            "Call.method": scoping_providers.ExtRelativeName("obj.ref",
-                                                             "methods",
-                                                             "extends")
-        })
+        mm = metamodel_from_file(join(this_folder, grammar_file_name), debug=False)
+        mm.register_scope_providers(
+            {
+                "*.*": provider,
+                "Call.method": scoping_providers.ExtRelativeName(
+                    "obj.ref", "methods", "extends"
+                ),
+            }
+        )
         return mm
 
     import_lookup_provider = scoping_providers.FQNImportURI()
 
     a_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "A.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "A.tx")
+    )
     b_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "B.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "B.tx")
+    )
     c_mm = get_meta_model(
-        import_lookup_provider, join(this_folder,
-                                     "metamodel_provider3", "C.tx"))
+        import_lookup_provider, join(this_folder, "metamodel_provider3", "C.tx")
+    )
 
     clear_language_registrations()
-    register_language(
-        'a-dsl',
-        pattern='*.a',
-        description='Test Lang A',
-        metamodel=a_mm)
-    register_language(
-        'b-dsl',
-        pattern='*.b',
-        description='Test Lang B',
-        metamodel=b_mm)
-    register_language(
-        'c-dsl',
-        pattern='*.c',
-        description='Test Lang C',
-        metamodel=c_mm)
+    register_language("a-dsl", pattern="*.a", description="Test Lang A", metamodel=a_mm)
+    register_language("b-dsl", pattern="*.b", description="Test Lang B", metamodel=b_mm)
+    register_language("c-dsl", pattern="*.c", description="Test Lang C", metamodel=c_mm)
 
     #################################
     # MODEL PARSING
     #################################
 
     m = a_mm.model_from_file(
-        join(this_folder, "metamodel_provider3",
-             "inheritance", "model_a.a"))
+        join(this_folder, "metamodel_provider3", "inheritance", "model_a.a")
+    )
 
     out_file = io.StringIO()
     # export.model_export(
@@ -79,5 +65,5 @@ def get_meta_model(provider, grammar_file_name):
     print(text)
     assert "a2_very_long_name" in text
     assert "b2_very_long_name" in text
-    assert "inheritance{}model_b.b".format(sep) in text
-    assert "inheritance{}model_b.b".format(sep) in text
+    assert f"inheritance{sep}model_b.b" in text
+    assert f"inheritance{sep}model_b.b" in text
diff --git a/tests/functional/test_scoping/test_model_modification_through_scoping.py b/tests/functional/test_scoping/test_model_modification_through_scoping.py
index 5b4b8a58a..81cc973d3 100644
--- a/tests/functional/test_scoping/test_model_modification_through_scoping.py
+++ b/tests/functional/test_scoping/test_model_modification_through_scoping.py
@@ -1,9 +1,8 @@
-from __future__ import unicode_literals
-
-from textx import metamodel_from_str, get_model, get_metamodel
 from pytest import raises
-from textx.exceptions import TextXSemanticError
+
 import textx.scoping.providers as providers
+from textx import get_metamodel, get_model, metamodel_from_str
+from textx.exceptions import TextXSemanticError
 from textx.scoping import Postponed
 
 """
@@ -24,17 +23,17 @@
 "Postponer" how this can be achieved).
 """
 
-grammar = r'''
+grammar = r"""
 Model: (persons+=Person|knows+=Knows)*;
 Person: ':' 'person' name=ID;
 Knows: person1=[Person] 'knows' person2=[Person];
-'''
-grammar_addon = r'''
+"""
+grammar_addon = r"""
 Model: (persons+=Person|knows+=Knows|greetings=Greeting)*;
 Person: ':' 'person' name=ID;
 Knows: person1=[Person] 'knows' person2=[Person]; // inventing
 Greeting: '*' 'hello' person=[Person]; // non-inventing
-'''
+"""
 
 
 def person_definer_scope(knows, attr, attr_ref):
@@ -45,21 +44,24 @@ def person_definer_scope(knows, attr, attr_ref):
         return found_persons[0]  # if a person exists, return it
     else:
         mm = get_metamodel(m)  # else, create it and store it in the model
-        person = mm['Person']()
+        person = mm["Person"]()
         person.name = name
         person.parent = m
         m.persons.append(person)
         return person
 
 
-class Postponer(object):
+class Postponer:
     """
     scope provider which forwards to a base scope provider
     and transforms a None to a Postponed.
     Reference resolution will fail if a set of Postponed
     resolutions does not change any more.
     """
-    def __init__(self, base=providers.PlainName()):
+
+    def __init__(self, base=None):
+        if base is None:
+            base = providers.PlainName()
         self.base = base
 
     def __call__(self, *args, **kwargs):
@@ -72,11 +74,13 @@ def __call__(self, *args, **kwargs):
 
 def test_model_modification_through_scoping_normal_lookup():
     mm = metamodel_from_str(grammar)
-    m = mm.model_from_str(r'''
+    m = mm.model_from_str(
+        r"""
         :person Tom
         :person Jerry
         Tom knows Jerry
-        ''')
+        """
+    )
     assert len(m.persons) == 2
     assert len(m.knows) == 1
 
@@ -84,95 +88,114 @@ def test_model_modification_through_scoping_normal_lookup():
 def test_model_modification_through_scoping_normal_lookup_failure():
     mm = metamodel_from_str(grammar)
     with raises(TextXSemanticError):
-        mm.model_from_str(r'''
+        mm.model_from_str(
+            r"""
             :person Tom
             :person Jerry
             Tom knows Jerry
             Tom knows Berry
-            ''')
+            """
+        )
 
 
 def test_model_modification_through_scoping_custom_lookup():
     mm = metamodel_from_str(grammar)
-    mm.register_scope_providers({'Knows.*': person_definer_scope})
-    m = mm.model_from_str(r'''
+    mm.register_scope_providers({"Knows.*": person_definer_scope})
+    m = mm.model_from_str(
+        r"""
         :person Tom
         :person Jerry
         Tom knows Jerry
         Tom knows Berry
         Berry knows Jerry
-        ''')
+        """
+    )
     assert len(m.persons) == 3
     assert len(m.knows) == 3
 
-    m = mm.model_from_str(r'''
+    m = mm.model_from_str(
+        r"""
         :person Tom
         :person Jerry
         :person Berry
         Tom knows Jerry
         Tom knows Berry
         Berry knows Jerry
-        ''')
+        """
+    )
     assert len(m.persons) == 3
     assert len(m.knows) == 3
 
-    m = mm.model_from_str(r'''
+    m = mm.model_from_str(
+        r"""
         Tom knows Jerry
         Tom knows Berry
         Berry knows Jerry
         :person Tom
         :person Jerry
         :person Berry
-        ''')
+        """
+    )
     assert len(m.persons) == 3
     assert len(m.knows) == 3
 
-    m = mm.model_from_str(r'''
+    m = mm.model_from_str(
+        r"""
         Tom knows Jerry
         Tom knows Berry
         Berry knows Jerry
-        ''')
+        """
+    )
     assert len(m.persons) == 3
     assert len(m.knows) == 3
 
 
 def test_model_modification_through_scoping_custom_lookup_addon_failure():
     mm = metamodel_from_str(grammar_addon)
-    mm.register_scope_providers({'Knows.*': person_definer_scope})
+    mm.register_scope_providers({"Knows.*": person_definer_scope})
 
     # Here, at least one case produces an error, since
     # Tom is not part of the model until the "Knowns" rule
     # is resolved.
-    with raises(TextXSemanticError, match=r'.*Unknown object.*Tom.*'):
-        mm.model_from_str(r'''
+    with raises(TextXSemanticError, match=r".*Unknown object.*Tom.*"):
+        mm.model_from_str(
+            r"""
             Tom knows Jerry
             *hello Tom
-            ''')
-        mm.model_from_str(r'''
+            """
+        )
+        mm.model_from_str(
+            r"""
             *hello Tom
             Tom knows Jerry
-            ''')
+            """
+        )
 
 
 def test_model_modification_through_scoping_custom_lookup_addon_fix():
     mm = metamodel_from_str(grammar_addon)
-    mm.register_scope_providers({'Knows.*': person_definer_scope,
-                                 'Greeting.*': Postponer()})
+    mm.register_scope_providers(
+        {"Knows.*": person_definer_scope, "Greeting.*": Postponer()}
+    )
 
-    m = mm.model_from_str(r'''
+    m = mm.model_from_str(
+        r"""
         Tom knows Jerry
         *hello Tom
-        ''')
+        """
+    )
 
     assert len(m.persons) == 2
     assert len(m.knows) == 1
     assert len(m.greetings) == 1
     assert m.greetings[0].person == m.knows[0].person1
 
-    m = mm.model_from_str(r'''
+    m = mm.model_from_str(
+        r"""
         *hello Tom
         Tom knows Jerry
-        ''')
+        """
+    )
 
     assert len(m.persons) == 2
     assert len(m.knows) == 1
@@ -180,8 +203,10 @@ def test_model_modification_through_scoping_custom_lookup_addon_fix():
     assert m.greetings[0].person == m.knows[0].person1
 
     # Unknown elements still produce an error, as expected
-    with raises(TextXSemanticError, match=r'.*Unresolvable.*Berry.*'):
-        mm.model_from_str(r'''
+    with raises(TextXSemanticError, match=r".*Unresolvable.*Berry.*"):
+        mm.model_from_str(
+            r"""
             Tom knows Jerry
             *hello Berry
-            ''')
+            """
+        )
diff --git a/tests/functional/test_scoping/test_model_repository.py b/tests/functional/test_scoping/test_model_repository.py
index b5ef36296..fc059fd3e 100644
--- a/tests/functional/test_scoping/test_model_repository.py
+++ b/tests/functional/test_scoping/test_model_repository.py
@@ -1,6 +1,5 @@
-from __future__ import unicode_literals
+from os.path import abspath, dirname, join
 
-from os.path import dirname, abspath, join
 import textx.scoping.providers as scoping_providers
 from textx import metamodel_from_file
 from textx.scoping import is_file_included
@@ -17,13 +16,15 @@ def test_inclusion_check_1():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'issue66', 'task_specification.tx'))
+        join(abspath(dirname(__file__)), "issue66", "task_specification.tx")
+    )
     search_path = [
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere1'),  # assembly
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere2')   # position
+        join(abspath(dirname(__file__)), "issue66", "somewhere1"),  # assembly
+        join(abspath(dirname(__file__)), "issue66", "somewhere2"),  # position
     ]
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.PlainNameImportURI(search_path=search_path)})
+        {"*.*": scoping_providers.PlainNameImportURI(search_path=search_path)}
+    )
 
     #################################
     # MODEL PARSING
@@ -34,44 +35,38 @@ def test_inclusion_check_1():
     #   be preferred.
     # * one only exists locally.
     m = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             'issue66', 'assembly_car1.prog'))
+        join(abspath(dirname(__file__)), "issue66", "assembly_car1.prog")
+    )
 
     # the model file itself is "included" (special case)
     assert is_file_included(
-        join(abspath(dirname(__file__)), 'issue66',
-             'assembly_car1.prog'),
-        m
+        join(abspath(dirname(__file__)), "issue66", "assembly_car1.prog"), m
     )
     # another model file
     assert not is_file_included(
-        join(abspath(dirname(__file__)), 'issue66', 'local',
-             'assembly_car3.prog'),
-        m
+        join(abspath(dirname(__file__)), "issue66", "local", "assembly_car3.prog"), m
     )
     # file in folder "local"
     assert not is_file_included(
-        join(abspath(dirname(__file__)), 'issue66', 'local',
-             'mylib', 'local.tasks'),
-        m
+        join(abspath(dirname(__file__)), "issue66", "local", "mylib", "local.tasks"), m
     )
     # file in folder "local"
     assert not is_file_included(
-        join(abspath(dirname(__file__)), 'issue66', 'local',
-             'mylib', 'position.tasks'),
-        m
+        join(abspath(dirname(__file__)), "issue66", "local", "mylib", "position.tasks"), m
     )
     # distant file (part of search path)
     assert is_file_included(
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere1',
-             'mylib', 'assembly.tasks'),
-        m
+        join(
+            abspath(dirname(__file__)), "issue66", "somewhere1", "mylib", "assembly.tasks"
+        ),
+        m,
     )
     # distant file (part of search path)
     assert is_file_included(
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere2',
-             'mylib', 'position.tasks'),
-        m
+        join(
+            abspath(dirname(__file__)), "issue66", "somewhere2", "mylib", "position.tasks"
+        ),
+        m,
     )
 
     #################################
@@ -90,13 +85,15 @@ def test_inclusion_check_2():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)), 'issue66', 'task_specification.tx'))
+        join(abspath(dirname(__file__)), "issue66", "task_specification.tx")
+    )
     search_path = [
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere1'),  # assembly
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere2')   # position
+        join(abspath(dirname(__file__)), "issue66", "somewhere1"),  # assembly
+        join(abspath(dirname(__file__)), "issue66", "somewhere2"),  # position
     ]
     my_meta_model.register_scope_providers(
-        {"*.*": scoping_providers.PlainNameImportURI(search_path=search_path)})
+        {"*.*": scoping_providers.PlainNameImportURI(search_path=search_path)}
+    )
 
     #################################
     # MODEL PARSING
@@ -107,38 +104,34 @@ def test_inclusion_check_2():
     #   be preferred.
     # * one only exists locally.
     m = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             'issue66', 'local', 'assembly_car3.prog'))
+        join(abspath(dirname(__file__)), "issue66", "local", "assembly_car3.prog")
+    )
 
     # the model file itself is "included" (special case)
     assert is_file_included(
-        join(abspath(dirname(__file__)), 'issue66', 'local',
-             'assembly_car3.prog'),
-        m
+        join(abspath(dirname(__file__)), "issue66", "local", "assembly_car3.prog"), m
     )
     # local file
     assert is_file_included(
-        join(abspath(dirname(__file__)), 'issue66', 'local',
-             'mylib', 'local.tasks'),
-        m
+        join(abspath(dirname(__file__)), "issue66", "local", "mylib", "local.tasks"), m
     )
     # local file
     assert is_file_included(
-        join(abspath(dirname(__file__)), 'issue66', 'local',
-             'mylib', 'position.tasks'),
-        m
+        join(abspath(dirname(__file__)), "issue66", "local", "mylib", "position.tasks"), m
     )
     # distant file
     assert not is_file_included(
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere1',
-             'mylib', 'assembly.tasks'),
-        m
+        join(
+            abspath(dirname(__file__)), "issue66", "somewhere1", "mylib", "assembly.tasks"
+        ),
+        m,
     )
     # distant file
     assert not is_file_included(
-        join(abspath(dirname(__file__)), 'issue66', 'somewhere2',
-             'mylib', 'position.tasks'),
-        m
+        join(
+            abspath(dirname(__file__)), "issue66", "somewhere2", "mylib", "position.tasks"
+        ),
+        m,
     )
 
     #################################
@@ -148,11 +141,10 @@ def test_inclusion_check_2():
 
 def test_no_tx_model_repos():
     from textx import metamodel_from_str
+
     mm = metamodel_from_str("Model: 'A';")
     m = mm.model_from_str("A")
 
     assert not is_file_included(
-        join(abspath(dirname(__file__)), 'issue66', 'local',
-             'mylib', 'position.tasks'),
-        m
+        join(abspath(dirname(__file__)), "issue66", "local", "mylib", "position.tasks"), m
     )
diff --git a/tests/functional/test_scoping/test_plain_name.py b/tests/functional/test_scoping/test_plain_name.py
index 1c6409a72..390ce03c2 100644
--- a/tests/functional/test_scoping/test_plain_name.py
+++ b/tests/functional/test_scoping/test_plain_name.py
@@ -1,12 +1,10 @@
-from __future__ import unicode_literals
 from pytest import raises
 
 import textx.exceptions
 import textx.scoping.providers as scoping_providers
-from textx import get_children
-from textx import metamodel_from_str
+from textx import get_children, metamodel_from_str
 
-metamodel_str = '''
+metamodel_str = """
 Model:
     packages*=Package
 ;
@@ -26,7 +24,7 @@
 Attribute:
         'attr' ref=[Class] name=ID ';'
 ;
-'''
+"""
 
 
 def test_plain_name_ref():
@@ -39,14 +37,16 @@ def test_plain_name_ref():
 
     my_metamodel = metamodel_from_str(metamodel_str)
 
-    my_metamodel.register_scope_providers({"*.*": scoping_providers.PlainName(
-        multi_metamodel_support=False)})
+    my_metamodel.register_scope_providers(
+        {"*.*": scoping_providers.PlainName(multi_metamodel_support=False)}
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
-    my_model = my_metamodel.model_from_str('''
+    my_model = my_metamodel.model_from_str(
+        """
     package P1 {
         class Part1 {
         }
@@ -61,35 +61,32 @@ class C2 {
             attr Part2 p2b;
         }
     }
-    ''')
+    """
+    )
 
     #################################
     # TEST MODEL
     #################################
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "rec",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "rec", my_model)
     assert len(a) == 1
     assert a[0].name == "rec"
     assert a[0].ref.__class__.__name__ == "Class"
     assert a[0].ref.name == "C2"
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "p1",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "p1", my_model)
     assert len(a) == 1
     assert a[0].name == "p1"
     assert a[0].ref.__class__.__name__ == "Class"
     assert a[0].ref.name == "Part1"
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "p2a",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "p2a", my_model)
     assert len(a) == 1
     assert a[0].name == "p2a"
     assert a[0].ref.__class__.__name__ == "Class"
     assert a[0].ref.name == "Part2"
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "p2b",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "p2b", my_model)
     assert len(a) == 1
     assert a[0].name == "p2b"
     assert a[0].ref.__class__.__name__ == "Class"
@@ -98,9 +95,9 @@ class C2 {
     ###########################
     # MODEL WITH ERROR (no entry found)
     ############################
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*Unknown object.*Part0.*'):
-        my_metamodel.model_from_str('''
+    with raises(textx.exceptions.TextXSemanticError, match=r".*Unknown object.*Part0.*"):
+        my_metamodel.model_from_str(
+            """
         package P1 {
             class Part1 {
             }
@@ -110,12 +107,14 @@ class C2 {
                 attr Part0 p1;
             }
         }
-        ''')
+        """
+        )
 
     ###########################
     # MODEL WITH NO ERROR (double entries not checked)
     ############################
-    my_metamodel.model_from_str('''
+    my_metamodel.model_from_str(
+        """
     package P1 {
         class Part1 {
         }
@@ -127,7 +126,8 @@ class C2 {
             attr Part1 p1;
         }
     }
-    ''')
+    """
+    )
 
     #################################
     # END
@@ -145,14 +145,16 @@ def test_plain_name_ref_with_muli_metamodel_support():
 
     my_metamodel = metamodel_from_str(metamodel_str)
 
-    my_metamodel.register_scope_providers({"*.*": scoping_providers.PlainName(
-        multi_metamodel_support=True)})
+    my_metamodel.register_scope_providers(
+        {"*.*": scoping_providers.PlainName(multi_metamodel_support=True)}
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
-    my_model = my_metamodel.model_from_str('''
+    my_model = my_metamodel.model_from_str(
+        """
     package P1 {
         class Part1 {
         }
@@ -167,35 +169,32 @@ class C2 {
             attr Part2 p2b;
         }
     }
-    ''')
+    """
+    )
 
     #################################
     # TEST MODEL
     #################################
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "rec",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "rec", my_model)
     assert len(a) == 1
     assert a[0].name == "rec"
     assert a[0].ref.__class__.__name__ == "Class"
     assert a[0].ref.name == "C2"
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "p1",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "p1", my_model)
     assert len(a) == 1
     assert a[0].name == "p1"
     assert a[0].ref.__class__.__name__ == "Class"
     assert a[0].ref.name == "Part1"
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "p2a",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "p2a", my_model)
     assert len(a) == 1
     assert a[0].name == "p2a"
     assert a[0].ref.__class__.__name__ == "Class"
     assert a[0].ref.name == "Part2"
 
-    a = get_children(lambda x: hasattr(x, 'name') and x.name == "p2b",
-                     my_model)
+    a = get_children(lambda x: hasattr(x, "name") and x.name == "p2b", my_model)
     assert len(a) == 1
     assert a[0].name == "p2b"
     assert a[0].ref.__class__.__name__ == "Class"
@@ -204,9 +203,9 @@ class C2 {
     ###########################
     # MODEL WITH ERROR (no entry found)
     ############################
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*Unknown object.*Part0.*'):
-        my_metamodel.model_from_str('''
+    with raises(textx.exceptions.TextXSemanticError, match=r".*Unknown object.*Part0.*"):
+        my_metamodel.model_from_str(
+            """
         package P1 {
             class Part1 {
             }
@@ -216,14 +215,18 @@ class C2 {
                 attr Part0 p1;
             }
         }
-        ''')
+        """
+        )
 
     ###########################
     # MODEL WITH ERROR (double entries)
     ############################
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*None:10:\d+: name Part1 is not unique.*'):
-        my_metamodel.model_from_str('''
+    with raises(
+        textx.exceptions.TextXSemanticError,
+        match=r".*None:10:\d+: name Part1 is not unique.*",
+    ):
+        my_metamodel.model_from_str(
+            """
         package P1 {
             class Part1 {
             }
@@ -235,7 +238,8 @@ class C2 {
                 attr Part1 p1;
             }
         }
-        ''')
+        """
+        )
 
     #################################
     # END
@@ -254,16 +258,17 @@ def test_plain_name_ref_type_error():
 
     my_metamodel = metamodel_from_str(metamodel_str)
 
-    my_metamodel.register_scope_providers({"*.*": scoping_providers.PlainName(
-        multi_metamodel_support=False)})
+    my_metamodel.register_scope_providers(
+        {"*.*": scoping_providers.PlainName(multi_metamodel_support=False)}
+    )
 
     #################################
     # MODEL PARSING AND TEST
     #################################
 
-    with raises(textx.exceptions.TextXSemanticError,
-                match=r'.*p1.*'):
-        my_metamodel.model_from_str('''
+    with raises(textx.exceptions.TextXSemanticError, match=r".*p1.*"):
+        my_metamodel.model_from_str(
+            """
         package P1 {
             class Part1 {
             }
@@ -278,4 +283,5 @@ class C2 {
                 attr p1 p2b;
             }
         }
-        ''')
+        """
+        )
diff --git a/tests/functional/test_scoping/test_reference_to_buildin_attribute.py b/tests/functional/test_scoping/test_reference_to_buildin_attribute.py
index a1daa9c0c..615a273ab 100644
--- a/tests/functional/test_scoping/test_reference_to_buildin_attribute.py
+++ b/tests/functional/test_scoping/test_reference_to_buildin_attribute.py
@@ -1,9 +1,9 @@
-from __future__ import unicode_literals
-from textx import metamodel_from_str
 from pytest import raises
+
+from textx import metamodel_from_str
 from textx.scoping.tools import get_unique_named_object
 
-metamodel_str = r'''
+metamodel_str = r"""
 Model:
     access+=Access
 ;
@@ -11,7 +11,7 @@
     'access' name=ID pyobj=[OBJECT] ('.' pyattr=[OBJECT])?
 ;
 Comment: /\/\/.*$/;
-'''
+"""
 
 
 def test_reference_to_buildin_attribute():
@@ -23,10 +23,7 @@ def test_reference_to_buildin_attribute():
     # We can (in combination with a special scope provider)
     # reference (and combine) our textX model with this model from
     # the (non textX) meta model object.
-    foreign_model = {
-        "name": "Test",
-        "value": 3
-    }
+    foreign_model = {"name": "Test", "value": 3}
 
     # custom scope provider
     def my_scope_provider(obj, attr, attr_ref):
@@ -34,22 +31,26 @@ def my_scope_provider(obj, attr, attr_ref):
         if attr_ref.obj_name in pyobj:
             return pyobj[attr_ref.obj_name]
         else:
-            raise Exception("{} not found".format(attr_ref.obj_name))
+            raise Exception(f"{attr_ref.obj_name} not found")
 
     # create meta model
-    my_metamodel = metamodel_from_str(metamodel_str,
-                                      builtins={
-                                          'foreign_model': foreign_model})
-    my_metamodel.register_scope_providers({
-        "Access.pyattr": my_scope_provider,
-    })
+    my_metamodel = metamodel_from_str(
+        metamodel_str, builtins={"foreign_model": foreign_model}
+    )
+    my_metamodel.register_scope_providers(
+        {
+            "Access.pyattr": my_scope_provider,
+        }
+    )
 
     # read model
-    my_model = my_metamodel.model_from_str('''
+    my_model = my_metamodel.model_from_str(
+        """
     access A1 foreign_model
     access A2 foreign_model.name
     access A3 foreign_model.value
-    ''')
+    """
+    )
 
     # check that the references are OK
     A2_name = get_unique_named_object(my_model, "A2").pyattr
@@ -58,21 +59,27 @@ def my_scope_provider(obj, attr, attr_ref):
     assert A3_val is foreign_model["value"]
 
     # check error cases
-    with raises(Exception, match=r'.*noname not found.*'):
-        my_metamodel.model_from_str('''
+    with raises(Exception, match=r".*noname not found.*"):
+        my_metamodel.model_from_str(
+            """
         access A1 foreign_model
         access A2 foreign_model.noname
-        ''')
+        """
+        )
 
-    with raises(Exception, match=r'.*unknown_model.*'):
-        my_metamodel.model_from_str('''
+    with raises(Exception, match=r".*unknown_model.*"):
+        my_metamodel.model_from_str(
+            """
         access A1 foreign_model
         access A2 unknown_model.name
-        ''')
-    with raises(Exception, match=r'.*unknown_model.*'):
-        my_metamodel.model_from_str('''
+        """
+        )
+    with raises(Exception, match=r".*unknown_model.*"):
+        my_metamodel.model_from_str(
+            """
         access A1 unknown_model
         access A2 foreign_model.name
-        ''')
+        """
+        )
 
     pass
diff --git a/tests/functional/test_scoping/test_reference_to_nontextx_attribute.py b/tests/functional/test_scoping/test_reference_to_nontextx_attribute.py
index c12b16eda..65d4310e4 100644
--- a/tests/functional/test_scoping/test_reference_to_nontextx_attribute.py
+++ b/tests/functional/test_scoping/test_reference_to_nontextx_attribute.py
@@ -1,10 +1,11 @@
-from __future__ import unicode_literals
-from textx import metamodel_from_str
-from os.path import dirname, abspath, join
+from os.path import abspath, dirname, join
+
 from pytest import raises
+
+from textx import metamodel_from_str
 from textx.scoping.tools import get_unique_named_object
 
-metamodel_str = r'''
+metamodel_str = r"""
 Model:
     imports+=Json
     access+=Access
@@ -15,7 +16,7 @@
 
 Json: 'import' filename=STRING 'as' name=ID;
 Comment: /\/\/.*$/;
-'''
+"""
 
 
 def test_reference_to_nontextx_attribute():
@@ -28,28 +29,34 @@ def test_reference_to_nontextx_attribute():
     def generic_scope_provider(obj, attr, attr_ref):
         if not obj.pyobj:
             from textx.scoping import Postponed
+
             return Postponed()
         if not hasattr(obj.pyobj, "data"):
             import json
-            obj.pyobj.data = json.load(open(
-                join(abspath(dirname(__file__)), obj.pyobj.filename)))
+
+            with open(join(abspath(dirname(__file__)), obj.pyobj.filename)) as f:
+                obj.pyobj.data = json.load(f)
         if attr_ref.obj_name in obj.pyobj.data:
             return obj.pyobj.data[attr_ref.obj_name]
         else:
-            raise Exception("{} not found".format(attr_ref.obj_name))
+            raise Exception(f"{attr_ref.obj_name} not found")
 
     # create meta model
     my_metamodel = metamodel_from_str(metamodel_str)
-    my_metamodel.register_scope_providers({
-        "Access.pyattr": generic_scope_provider,
-    })
+    my_metamodel.register_scope_providers(
+        {
+            "Access.pyattr": generic_scope_provider,
+        }
+    )
 
     # read model
-    my_model = my_metamodel.model_from_str('''
+    my_model = my_metamodel.model_from_str(
+        """
     import "test_reference_to_nontextx_attribute/othermodel.json" as data
     access A1 data.name
     access A2 data.gender
-    ''')
+    """
+    )
 
     # check that the references are OK
     A1_name = get_unique_named_object(my_model, "A1").pyattr
@@ -57,18 +64,22 @@ def generic_scope_provider(obj, attr, attr_ref):
     A2_gender = get_unique_named_object(my_model, "A2").pyattr
     assert A2_gender == "male"
 
-    with raises(Exception, match=r'.*noname.*'):
-        my_metamodel.model_from_str('''
+    with raises(Exception, match=r".*noname.*"):
+        my_metamodel.model_from_str(
+            """
         import "test_reference_to_nontextx_attribute/othermodel.json" as data
         access A1 data.noname
         access A2 data.gender
-        ''')
+        """
+        )
 
-    with raises(Exception, match=r'.*filenotfound.*'):
-        my_metamodel.model_from_str('''
+    with raises(Exception, match=r".*filenotfound.*"):
+        my_metamodel.model_from_str(
+            """
         import "test_reference_to_nontextx_attribute/filenotfound.json" as data
         access A1 data.name
         access A2 data.gender
-        ''')
+        """
+        )
 
     pass
diff --git a/tests/functional/test_scoping/test_rrel.py b/tests/functional/test_scoping/test_rrel.py
index 1fdd31873..8c5b1c4d1 100644
--- a/tests/functional/test_scoping/test_rrel.py
+++ b/tests/functional/test_scoping/test_rrel.py
@@ -1,11 +1,10 @@
-from __future__ import unicode_literals
-from textx.scoping.rrel import rrel_standalone, parse
-from arpeggio import ParserPython, NoMatch
-from textx import metamodel_from_str, textx_isinstance
-from textx.scoping.rrel import find, find_object_with_path
-from textx.scoping import ModelRepository
+from arpeggio import NoMatch, ParserPython
 from pytest import raises
+
+from textx import metamodel_from_str, textx_isinstance
 from textx.exceptions import TextXSemanticError
+from textx.scoping import ModelRepository
+from textx.scoping.rrel import find, find_object_with_path, parse, rrel_standalone
 
 
 def test_rrel_basic_parser1():
@@ -22,23 +21,23 @@ def test_rrel_basic_parser1():
 
 def test_rrel_basic_parser2():
     tree = parse("^pkg*.cls")
-    assert str(tree) == '(..)*.(pkg)*.cls'
+    assert str(tree) == "(..)*.(pkg)*.cls"
     tree = parse("obj.ref.~extension *.methods")
-    assert str(tree) == 'obj.ref.(~extension)*.methods'
+    assert str(tree) == "obj.ref.(~extension)*.methods"
     tree = parse("type.vals")
-    assert str(tree) == 'type.vals'
+    assert str(tree) == "type.vals"
     tree = parse("(type.vals)")
-    assert str(tree) == '(type.vals)'
+    assert str(tree) == "(type.vals)"
     tree = parse("(type.vals)*")
-    assert str(tree) == '(type.vals)*'
+    assert str(tree) == "(type.vals)*"
     tree = parse("instance . ( type.vals ) *")
-    assert str(tree) == 'instance.(type.vals)*'
+    assert str(tree) == "instance.(type.vals)*"
     tree = parse("a,b,c")
-    assert str(tree) == 'a,b,c'
+    assert str(tree) == "a,b,c"
     tree = parse("a.b.c")
-    assert str(tree) == 'a.b.c'
+    assert str(tree) == "a.b.c"
     tree = parse("parent(NAME)")
-    assert str(tree) == 'parent(NAME)'
+    assert str(tree) == "parent(NAME)"
     tree = parse("a.'b'~b.'c'~x")
     assert str(tree) == "a.'b'~b.'c'~x"
 
@@ -49,7 +48,7 @@ def test_rrel_basic_parser2():
         tree = parse("a,b,c,")
 
 
-metamodel_str = '''
+metamodel_str = """
     Model:
         packages*=Package
     ;
@@ -73,9 +72,9 @@ def test_rrel_basic_parser2():
 
     Comment: /#.*/;
     FQN: ID('.'ID)*;
-    '''
+    """
 
-modeltext = '''
+modeltext = """
     package P1 {
         class Part1 {
         }
@@ -98,7 +97,7 @@ class rec {
             attr p1;
         }
     }
-    '''
+    """
 
 
 def test_rrel_basic_lookup():
@@ -137,6 +136,7 @@ def test_rrel_basic_lookup():
     assert P2.name == "P2"
 
     from textx import get_model
+
     assert get_model(my_model) is my_model
 
     P2 = find(my_model, "P2", "packages*")
@@ -199,8 +199,12 @@ def test_rrel_basic_lookup():
     rec2 = find(my_model, "P2.Part2.rec", "other1,packages*.classes.attributes,other2")
     assert rec2 is rec
 
-    rec2 = find(my_model, "P2::Part2::rec", "other1,packages*.classes.attributes,other2",
-                split_string="::")
+    rec2 = find(
+        my_model,
+        "P2::Part2::rec",
+        "other1,packages*.classes.attributes,other2",
+        split_string="::",
+    )
     assert rec2 is rec
 
     rec2 = find(my_model, "P2.Part2.rec", "other1,other2,other3")
@@ -215,25 +219,39 @@ def test_rrel_basic_lookup():
     rec2 = find(my_model, "rec", "(~packages,~classes,attributes,classes)*")
     assert rec2.name == "rec"
 
-    rec2 = find(my_model, "rec",
-                "(~packages,~classes,attributes,classes)*", my_metamodel["OBJECT"])
+    rec2 = find(
+        my_model,
+        "rec",
+        "(~packages,~classes,attributes,classes)*",
+        my_metamodel["OBJECT"],
+    )
     assert rec2.name == "rec"
 
-    rec2 = find(my_model, "rec",
-                "(~packages,~classes,attributes,classes)*", my_metamodel["Attribute"])
+    rec2 = find(
+        my_model,
+        "rec",
+        "(~packages,~classes,attributes,classes)*",
+        my_metamodel["Attribute"],
+    )
     assert rec2 is rec
 
-    rec2 = find(my_model, "rec",
-                "(~packages,~classes,attributes,classes)*", my_metamodel["Package"])
+    rec2 = find(
+        my_model,
+        "rec",
+        "(~packages,~classes,attributes,classes)*",
+        my_metamodel["Package"],
+    )
     assert rec2 is None
 
-    rec2 = find(my_model, "rec",
-                "(~packages,classes,attributes,~classes)*", my_metamodel["Class"])
+    rec2 = find(
+        my_model, "rec", "(~packages,classes,attributes,~classes)*", my_metamodel["Class"]
+    )
     assert rec2.name == "rec"
     assert rec2 is not rec  # it is the class...
 
-    rec2 = find(my_model, "rec",
-                "(~packages,~classes,attributes,classes)*", my_metamodel["Class"])
+    rec2 = find(
+        my_model, "rec", "(~packages,~classes,attributes,classes)*", my_metamodel["Class"]
+    )
     assert rec2.name == "rec"
     assert rec2 is not rec  # it is the class...
 
@@ -249,8 +267,12 @@ def test_rrel_basic_lookup():
     t = find(my_model, "", "(.)*.no_existent")  # inifite recursion stopper
     assert t is None
 
-    rec2 = find(my_model, "rec",
-                "(.)*.(~packages,~classes,attributes,classes)*", my_metamodel["Class"])
+    rec2 = find(
+        my_model,
+        "rec",
+        "(.)*.(~packages,~classes,attributes,classes)*",
+        my_metamodel["Class"],
+    )
     assert rec2.name == "rec"
     assert rec2 is not rec  # it is the class...
 
@@ -271,23 +293,27 @@ def test_rrel_repetitions():
     in RREL expressions.
     """
 
-    my_metamodel = metamodel_from_str(r'''
+    my_metamodel = metamodel_from_str(
+        r"""
         Model: entries*=Entry;
         Entry: name=ID (':' ref=[Entry])?;
-    ''')
+    """
+    )
 
-    my_model = my_metamodel.model_from_str(r'''
+    my_model = my_metamodel.model_from_str(
+        r"""
         a: b
         c
         b: a
-    ''')
+    """
+    )
 
     a = find(my_model, "a", "entries.ref*")
-    assert a.name == 'a'
+    assert a.name == "a"
     b = find(my_model, "b", "entries.ref*")
-    assert b.name == 'b'
+    assert b.name == "b"
     c = find(my_model, "c", "entries.ref*")
-    assert c.name == 'c'
+    assert c.name == "c"
 
     a2 = find(my_model, "a.b.a", "entries.ref*")
     assert a2 == a
@@ -299,7 +325,7 @@ def test_rrel_repetitions():
     assert res == b
     assert len(objpath) == 3
     assert objpath[-1] == res
-    assert ".".join(map(lambda x: x.name, objpath)) == 'b.a.b'
+    assert ".".join(map(lambda x: x.name, objpath)) == "b.a.b"
 
     a2 = find(my_model, "b.a.b.a", "entries.ref*")
     assert a2 == a
@@ -308,7 +334,7 @@ def test_rrel_repetitions():
     assert res == a
     assert len(objpath) == 4
     assert objpath[-1] == res
-    assert ".".join(map(lambda x: x.name, objpath)) == 'b.a.b.a'
+    assert ".".join(map(lambda x: x.name, objpath)) == "b.a.b.a"
 
     a2 = find(my_model, "b.a.b.a.b.a.b.a.b.a", "entries.ref*")
     assert a2 == a
@@ -316,13 +342,17 @@ def test_rrel_repetitions():
 
 def test_split_str():
     from textx import metamodel_from_str
-    mm = metamodel_from_str('''
+
+    mm = metamodel_from_str(
+        """
         Model: a+=A r+=R;
         A: 'A' name=ID '{' a*=A  '}';
         R: 'R' a+=[A:FQN|+p:a*][','];
         FQN[split='::']: ID ('::' ID)*;
-    ''')
-    m = mm.model_from_str('''
+    """
+    )
+    m = mm.model_from_str(
+        """
         A a1 {
             A aa1 {
                 A aaa1 {}
@@ -338,20 +368,22 @@ def test_split_str():
         R a1::aa1::aaa1, a1::aa1::aab1, R, R::r2
         R R
         R a2::aa2
-    ''')
+    """
+    )
     assert len(m.r) == 3
 
     assert len(m.r[0].a) == 4
     assert len(m.r[1].a) == 1
     assert len(m.r[2].a) == 1
 
-    assert '.'.join(map(lambda x: x.name, m.r[0].a[0]._tx_path)) == 'a1.aa1.aaa1'
-    assert '.'.join(map(lambda x: x.name, m.r[0].a[1]._tx_path)) == 'a1.aa1.aab1'
-    assert '.'.join(map(lambda x: x.name, m.r[0].a[2]._tx_path)) == 'R'
-    assert '.'.join(map(lambda x: x.name, m.r[0].a[3]._tx_path)) == 'R.r2'
+    assert ".".join(map(lambda x: x.name, m.r[0].a[0]._tx_path)) == "a1.aa1.aaa1"
+    assert ".".join(map(lambda x: x.name, m.r[0].a[1]._tx_path)) == "a1.aa1.aab1"
+    assert ".".join(map(lambda x: x.name, m.r[0].a[2]._tx_path)) == "R"
+    assert ".".join(map(lambda x: x.name, m.r[0].a[3]._tx_path)) == "R.r2"
 
-    with raises(TextXSemanticError, match=r'.*Unknown object.*a2::unknown.*'):
-        _ = mm.model_from_str('''
+    with raises(TextXSemanticError, match=r".*Unknown object.*a2::unknown.*"):
+        _ = mm.model_from_str(
+            """
             A a1 {
                 A aa1 {
                     A aaa1 {}
@@ -362,23 +394,27 @@ def test_split_str():
                 A aa2 {}
             }
             R a2::unknown
-        ''')
+        """
+        )
 
 
 def test_split_str_multifile():
     # same as test above, but with "+mp:" flag
     from os.path import dirname, join
+
     from textx import metamodel_from_file
+
     this_folder = dirname(__file__)
-    mm = metamodel_from_file(join(this_folder, 'rrel', 'Grammar.tx'))
-    m = mm.model_from_file(join(this_folder, 'rrel', 'main.model'))
+    mm = metamodel_from_file(join(this_folder, "rrel", "Grammar.tx"))
+    m = mm.model_from_file(join(this_folder, "rrel", "main.model"))
     # see above:
-    assert '.'.join(map(lambda x: x.name, m.r[0].a[0]._tx_path)) == 'a1.aa1.aaa1'
+    assert ".".join(map(lambda x: x.name, m.r[0].a[0]._tx_path)) == "a1.aa1.aaa1"
 
 
 def test_rrel_with_fixed_string_in_navigation():
     builtin_models = ModelRepository()
-    mm = metamodel_from_str(r'''
+    mm = metamodel_from_str(
+        r"""
         Model: types_collection*=TypesCollection
             ('activeTypes' '=' active_types=[TypesCollection])? usings*=Using;
         Using: 'using' name=ID "=" type=[Type:ID|+m:
@@ -389,18 +425,25 @@ def test_rrel_with_fixed_string_in_navigation():
         TypesCollection: 'types' name=ID "{" types*=Type "}";
         Type: 'type' name=ID;
         Comment: /#.*?$/;
-    ''', builtin_models=builtin_models)
+    """,
+        builtin_models=builtin_models,
+    )
 
-    builtin_models.add_model(mm.model_from_str(r'''
+    builtin_models.add_model(
+        mm.model_from_str(
+            r"""
         types builtin {
             type i32
             type i64
             type f32
             type f64
         }
-    '''))
+    """
+        )
+    )
 
-    _ = mm.model_from_str(r'''
+    _ = mm.model_from_str(
+        r"""
         types MyTypes {
             type Int
             type Double
@@ -414,11 +457,12 @@ def test_rrel_with_fixed_string_in_navigation():
         using myInt = Int    # found via "regular lookup"
         using myi32 = i32    # found via "default lookup"
         # using myFoo = Foo  # --> not found
-    ''')
+    """
+    )
 
-    with raises(TextXSemanticError,
-                match=r'.*Unknown object "Foo".*'):
-        _ = mm.model_from_str(r'''
+    with raises(TextXSemanticError, match=r'.*Unknown object "Foo".*'):
+        _ = mm.model_from_str(
+            r"""
             types MyTypes {
                 type Int
                 type Double
@@ -432,12 +476,14 @@ def test_rrel_with_fixed_string_in_navigation():
             using myInt = Int    # found via "regular lookup"
             using myi32 = i32    # found via "default lookup"
             using myFoo = Foo    # --> not found
-        ''')
+        """
+        )
 
 
 def test_rrel_with_fixed_string_in_navigation_with_scalars():
     builtin_models = ModelRepository()
-    mm = metamodel_from_str(r'''
+    mm = metamodel_from_str(
+        r"""
         Model: types_collection=TypesCollection // scalar here (compared to last test)
             ('activeTypes' '=' active_types=[TypesCollection])? usings*=Using;
         Using: 'using' name=ID "=" type=[Type:ID|+m:
@@ -448,18 +494,25 @@ def test_rrel_with_fixed_string_in_navigation_with_scalars():
         TypesCollection: 'types' name=ID "{" types*=Type "}";
         Type: 'type' name=ID;
         Comment: /#.*?$/;
-    ''', builtin_models=builtin_models)
+    """,
+        builtin_models=builtin_models,
+    )
 
-    builtin_models.add_model(mm.model_from_str(r'''
+    builtin_models.add_model(
+        mm.model_from_str(
+            r"""
         types builtin {
             type i32
             type i64
             type f32
             type f64
         }
-    '''))
+    """
+        )
+    )
 
-    _ = mm.model_from_str(r'''
+    _ = mm.model_from_str(
+        r"""
         types MyTypes {
             type Int
             type Double
@@ -468,11 +521,12 @@ def test_rrel_with_fixed_string_in_navigation_with_scalars():
         using myDouble = Double
         using myInt = Int    # found via "regular lookup"
         using myi32 = i32    # found via "default lookup"
-    ''')
+    """
+    )
 
-    with raises(TextXSemanticError,
-                match=r'.*Unknown object "Unknown".*'):
-        _ = mm.model_from_str(r'''
+    with raises(TextXSemanticError, match=r'.*Unknown object "Unknown".*'):
+        _ = mm.model_from_str(
+            r"""
             types MyTypes {
                 type Int
                 type Double
@@ -482,22 +536,25 @@ def test_rrel_with_fixed_string_in_navigation_with_scalars():
             using myInt = Int    # found via "regular lookup"
             using myi32 = i32    # found via "default lookup"
             using myFoo = Unknown    # --> not found
-        ''')
+        """
+        )
 
 
 def test_lookup_multifile():
     # same as test above, but with "+mp:" flag
     from os.path import dirname, join
+
     from textx import metamodel_from_file
+
     this_folder = dirname(__file__)
-    mm = metamodel_from_file(join(this_folder, 'rrel_multifile', 'Grammar.tx'))
+    mm = metamodel_from_file(join(this_folder, "rrel_multifile", "Grammar.tx"))
 
     # "standard" multi-file usage
-    m = mm.model_from_file(join(this_folder, 'rrel_multifile', 'main.model'))
+    m = mm.model_from_file(join(this_folder, "rrel_multifile", "main.model"))
     assert m is not None
 
     # Rule "P1" employs a mandatory rrel path entry ".." (no multi-file)
-    m = mm.model_from_file(join(this_folder, 'rrel_multifile', 'navigation0.model'))
+    m = mm.model_from_file(join(this_folder, "rrel_multifile", "navigation0.model"))
     assert m is not None
 
     # Rule "P1" employs a mandatory rrel path entry ".." (with multi-file)
@@ -505,24 +562,27 @@ def test_lookup_multifile():
     # TODO: in case of multi-files, do not start at the root of each model, but
     # iterate over all models once the path reaches the model root (e.g. with .. in
     # a navigation rrel node)...
-    m = mm.model_from_file(join(this_folder, 'rrel_multifile', 'navigation1.model'))
+    m = mm.model_from_file(join(this_folder, "rrel_multifile", "navigation1.model"))
     assert m is not None
 
     # the next exammple is missing the include statement (leads to "Unknown object...")
-    with raises(TextXSemanticError, match=r'Unknown object'):
-        _ = mm.model_from_file(join(this_folder, 'rrel_multifile',
-                                    'navigation1_err.model'))
+    with raises(TextXSemanticError, match=r"Unknown object"):
+        _ = mm.model_from_file(
+            join(this_folder, "rrel_multifile", "navigation1_err.model")
+        )
 
 
 def test_lookup_multifile_missing_flag_m():
     from os.path import dirname, join
+
     from textx import metamodel_from_file
+
     this_folder = dirname(__file__)
 
     # the next exammple is missing the +m flag in the grammar
     # (lookup across files disabled):
-    mmE = metamodel_from_file(join(this_folder, 'rrel_multifile',
-                                   'GrammarMissingPlusM.tx'))
-    with raises(TextXSemanticError, match=r'Unknown object'):
-        _ = mmE.model_from_file(join(this_folder, 'rrel_multifile',
-                                     'navigation1.model'))
+    mmE = metamodel_from_file(
+        join(this_folder, "rrel_multifile", "GrammarMissingPlusM.tx")
+    )
+    with raises(TextXSemanticError, match=r"Unknown object"):
+        _ = mmE.model_from_file(join(this_folder, "rrel_multifile", "navigation1.model"))
diff --git a/tests/functional/test_scoping/test_scoping_tools.py b/tests/functional/test_scoping/test_scoping_tools.py
index cf454beeb..2471d7fe2 100644
--- a/tests/functional/test_scoping/test_scoping_tools.py
+++ b/tests/functional/test_scoping/test_scoping_tools.py
@@ -1,39 +1,44 @@
-from __future__ import unicode_literals
-from os.path import dirname, abspath, join
+from os.path import abspath, dirname, join
 
-import textx.scoping.providers as scoping_providers
-from textx import metamodel_from_file, metamodel_from_str
-from textx.scoping.tools import resolve_model_path, \
-    get_list_of_concatenated_objects
-from textx.scoping.tools import get_unique_named_object
-from textx import textx_isinstance
-from textx import get_children_of_type, get_model
-from pytest import raises
 import attr
+from pytest import raises
 
+import textx.scoping.providers as scoping_providers
+from textx import (
+    get_children_of_type,
+    get_model,
+    metamodel_from_file,
+    metamodel_from_str,
+    textx_isinstance,
+)
+from textx.scoping.tools import (
+    get_list_of_concatenated_objects,
+    get_unique_named_object,
+    resolve_model_path,
+)
 
-def test_textx_tools_with_frozen_classes1():
 
+def test_textx_tools_with_frozen_classes1():
     @attr.s(frozen=True)
-    class Model(object):
+    class Model:
         # _tx_filename = attr.ib()
         # _tx_parser = attr.ib()
         use = attr.ib()
         data = attr.ib()
 
     @attr.s(frozen=True)
-    class Content(object):
+    class Content:
         parent = attr.ib()
         elementsA = attr.ib()
         elementsB = attr.ib()
         ref = attr.ib()
 
     @attr.s(frozen=True)
-    class Element(object):
+    class Element:
         parent = attr.ib()
         name = attr.ib()
 
-    grammar = r'''
+    grammar = r"""
     Model:
         'use' use=Use
         data=Content;
@@ -43,88 +48,86 @@ class Element(object):
         'ref' ref=[Element];
     Element: '*' name=ID;
     Use: 'A'|'B';
-    '''
-    text_ok1 = r'''
+    """
+    text_ok1 = r"""
         use A
         A: *a *b *c
         B: *d *e *f
         ref b
-    '''
-    text_ok2 = r'''
+    """
+    text_ok2 = r"""
         use B
         A: *a *b *c
         B: *d *e *f
         ref d
-    '''
-    text_not_ok = r'''
+    """
+    text_not_ok = r"""
         use B
         A: *a *b *c
         B: *d *e *f
         ref b
-    '''
+    """
     for classes in [[], [Model, Content, Element]]:
         print("Test Loop, classes==", classes)
 
-        ref_scope_was_used = [False]
+        ref_scope_was_used = False
 
         def ref_scope(refItem, myattr, attr_ref):
             # python3: nonlocal ref_scope_was_used
-            ref_scope_was_used[0] = True
-            if get_model(refItem).use == 'A':
+            nonlocal ref_scope_was_used
+            ref_scope_was_used = True
+            if get_model(refItem).use == "A":
                 return resolve_model_path(
-                    refItem, "parent(Model).data.elementsA.{}".format(
-                        attr_ref.obj_name), True)
+                    refItem, f"parent(Model).data.elementsA.{attr_ref.obj_name}", True
+                )
             else:
                 return resolve_model_path(
-                    refItem, "parent(Model).data.elementsB.{}".format(
-                        attr_ref.obj_name), True)
+                    refItem, f"parent(Model).data.elementsB.{attr_ref.obj_name}", True
+                )
 
         mm = metamodel_from_str(grammar, classes=classes)
-        mm.register_scope_providers({
-            "Content.ref": ref_scope
-        })
-        ref_scope_was_used[0] = False
+        mm.register_scope_providers({"Content.ref": ref_scope})
+        ref_scope_was_used = False
         m = mm.model_from_str(text_ok1)
-        assert ref_scope_was_used[0]
+        assert ref_scope_was_used
         if len(classes) == 0:
             # Assert that model object has its own filename and metamodel
-            assert '_tx_filename' in m.__dict__
-            assert '_tx_metamodel' in m.__dict__
+            assert "_tx_filename" in m.__dict__
+            assert "_tx_metamodel" in m.__dict__
         else:
             # Assert that special attributes are accessible only through class
-            assert '_tx_filename' not in m.__dict__
-            assert '_tx_metamodel' not in m.__dict__
+            assert "_tx_filename" not in m.__dict__
+            assert "_tx_metamodel" not in m.__dict__
 
-        ref_scope_was_used[0] = False
+        ref_scope_was_used = False
         mm.model_from_str(text_ok2)
-        assert ref_scope_was_used[0]
+        assert ref_scope_was_used
 
-        ref_scope_was_used[0] = False
+        ref_scope_was_used = False
         with raises(Exception, match=r'.*Unknown object "b".*'):
             mm.model_from_str(text_not_ok)
-        assert ref_scope_was_used[0]
+        assert ref_scope_was_used
 
 
 def test_textx_tools_with_frozen_classes2():
-
-    class Model(object):
+    class Model:
         def __init__(self, **kwargs):
             for k, v in kwargs.items():
                 setattr(self, k, v)
 
     @attr.s(frozen=True)
-    class Content(object):
+    class Content:
         parent = attr.ib()
         elementsA = attr.ib()
         elementsB = attr.ib()
         ref = attr.ib()
 
     @attr.s(frozen=True)
-    class Element(object):
+    class Element:
         parent = attr.ib()
         name = attr.ib()
 
-    grammar = r'''
+    grammar = r"""
     Model:
         'use' use=Use
         data=Content;
@@ -134,75 +137,73 @@ class Element(object):
         'ref' ref=[Element];
     Element: '*' name=ID;
     Use: 'A'|'B';
-    '''
-    text_ok1 = r'''
+    """
+    text_ok1 = r"""
         use A
         A: *a *b *c
         B: *d *e *f
         ref b
-    '''
-    text_ok2 = r'''
+    """
+    text_ok2 = r"""
         use B
         A: *a *b *c
         B: *d *e *f
         ref d
-    '''
-    text_not_ok = r'''
+    """
+    text_not_ok = r"""
         use B
         A: *a *b *c
         B: *d *e *f
         ref b
-    '''
+    """
     for classes in [[], [Model, Content, Element]]:
         print("Test Loop, classes==", classes)
 
-        ref_scope_was_used = [False]
+        ref_scope_was_used = False
 
         def ref_scope(refItem, myattr, attr_ref):
             # python3: nonlocal ref_scope_was_used
-            ref_scope_was_used[0] = True
-            if get_model(refItem).use == 'A':
+            nonlocal ref_scope_was_used
+            ref_scope_was_used = True
+            if get_model(refItem).use == "A":
                 return resolve_model_path(
-                    refItem, "parent(Model).data.elementsA.{}".format(
-                        attr_ref.obj_name), True)
+                    refItem, f"parent(Model).data.elementsA.{attr_ref.obj_name}", True
+                )
             else:
                 return resolve_model_path(
-                    refItem, "parent(Model).data.elementsB.{}".format(
-                        attr_ref.obj_name), True)
+                    refItem, f"parent(Model).data.elementsB.{attr_ref.obj_name}", True
+                )
 
         mm = metamodel_from_str(grammar, classes=classes)
-        mm.register_scope_providers({
-            "Content.ref": ref_scope
-        })
-        ref_scope_was_used[0] = False
+        mm.register_scope_providers({"Content.ref": ref_scope})
+        ref_scope_was_used = False
         m = mm.model_from_str(text_ok1)
-        assert ref_scope_was_used[0]
+        assert ref_scope_was_used
 
-        assert hasattr(m, '_tx_filename')
-        assert hasattr(m, '_tx_metamodel')
+        assert hasattr(m, "_tx_filename")
+        assert hasattr(m, "_tx_metamodel")
 
-        ref_scope_was_used[0] = False
+        ref_scope_was_used = False
         mm.model_from_str(text_ok2)
-        assert ref_scope_was_used[0]
+        assert ref_scope_was_used
 
-        ref_scope_was_used[0] = False
+        ref_scope_was_used = False
         with raises(Exception, match=r'.*Unknown object "b".*'):
             mm.model_from_str(text_not_ok)
-        assert ref_scope_was_used[0]
+        assert ref_scope_was_used
 
 
 def test_textx_isinstance():
-    grammar = \
-        '''
+    grammar = """
     Model: a=A;
     A: B;
     B: C;
     C: x=ID;
-    '''
+    """
     my_meta_model = metamodel_from_str(grammar)
-    A = my_meta_model['A']
-    B = my_meta_model['B']
-    C = my_meta_model['C']
+    A = my_meta_model["A"]
+    B = my_meta_model["B"]
+    C = my_meta_model["C"]
     my_model = my_meta_model.model_from_str("c")
     c = get_children_of_type("C", my_model)
     assert len(c) == 1
@@ -218,59 +219,56 @@ def test_resolve_model_path_with_lists():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'components_model1', 'Components.tx'))
-    my_meta_model.register_scope_providers({
-        "*.*": scoping_providers.FQN(),
-        "Connection.from_port":
-            scoping_providers.ExtRelativeName("from_inst.component",
-                                              "slots",
-                                              "extends"),
-        "Connection.to_port":
-            scoping_providers.ExtRelativeName("to_inst.component",
-                                              "slots",
-                                              "extends"),
-    })
+        join(abspath(dirname(__file__)), "components_model1", "Components.tx")
+    )
+    my_meta_model.register_scope_providers(
+        {
+            "*.*": scoping_providers.FQN(),
+            "Connection.from_port": scoping_providers.ExtRelativeName(
+                "from_inst.component", "slots", "extends"
+            ),
+            "Connection.to_port": scoping_providers.ExtRelativeName(
+                "to_inst.component", "slots", "extends"
+            ),
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "components_model1", "example_inherit2.components"))
+        join(
+            abspath(dirname(__file__)), "components_model1", "example_inherit2.components"
+        )
+    )
 
     #################################
     # TEST MODEL
     #################################
 
-    action2a = resolve_model_path(my_model,
-                                  "packages.usage.instances.action2",
-                                  True)
+    action2a = resolve_model_path(my_model, "packages.usage.instances.action2", True)
     action2b = get_unique_named_object(my_model, "action2")
     assert action2a is action2b
 
-    middle_a = resolve_model_path(my_model,
-                                  "packages.base.components.Middle",
-                                  True)
+    middle_a = resolve_model_path(my_model, "packages.base.components.Middle", True)
     middle_b = get_unique_named_object(my_model, "Middle")
     assert middle_a is middle_b
 
     # test parent(...) with lists
     action2a_with_parent = resolve_model_path(
-        action2a, "parent(Model).packages.usage.instances.action2", True)
+        action2a, "parent(Model).packages.usage.instances.action2", True
+    )
     assert action2a_with_parent == action2a
 
     # test "normal" parent with lists
-    action2a_with_parent2 = resolve_model_path(
-        action2a, "parent.instances.action2", True)
+    action2a_with_parent2 = resolve_model_path(action2a, "parent.instances.action2", True)
     assert action2a_with_parent2 == action2a
 
-    with raises(Exception, match=r'.*unexpected: got list in path for '
-                                 r'get_referenced_object.*'):
-        resolve_model_path(my_model,
-                           "packages.usage.instances.action2",
-                           False)
+    with raises(
+        Exception, match=r".*unexpected: got list in path for " r"get_referenced_object.*"
+    ):
+        resolve_model_path(my_model, "packages.usage.instances.action2", False)
 
 
 def test_resolve_model_path_simple_case():
@@ -278,11 +276,11 @@ def test_resolve_model_path_simple_case():
     # META MODEL DEF
     #################################
 
-    grammar = r'''
+    grammar = r"""
         Model: name=ID a=A b=B;
         A: 'A:' name=ID;
         B: 'B:' name=ID ('->' b=B| '=' a=A );
-    '''
+    """
 
     mm = metamodel_from_str(grammar)
 
@@ -290,14 +288,16 @@ def test_resolve_model_path_simple_case():
     # MODEL PARSING
     #################################
 
-    model = mm.model_from_str(r'''
+    model = mm.model_from_str(
+        r"""
         My_Model
             A: OuterA
             B: Level0_B
              -> B: Level1_B
              -> B: Level2_B
              = A: InnerA
-    ''')
+    """
+    )
 
     #################################
     # TEST MODEL
@@ -337,10 +337,10 @@ def test_resolve_model_path_simple_case_with_refs():
     # META MODEL DEF
     #################################
 
-    grammar = r'''
+    grammar = r"""
         Model: name=ID b=B;
         B: 'B:' name=ID ('->' b=B | '-->' bref=[B] );
-    '''
+    """
 
     mm = metamodel_from_str(grammar)
 
@@ -348,12 +348,14 @@ def test_resolve_model_path_simple_case_with_refs():
     # MODEL PARSING
     #################################
 
-    model = mm.model_from_str(r'''
+    model = mm.model_from_str(
+        r"""
         My_Model
             B: Level0_B
              -> B: Level1_B
              --> Level0_B
-    ''')
+    """
+    )
 
     #################################
     # TEST MODEL
@@ -375,30 +377,34 @@ def test_get_list_of_concatenated_objects():
     #################################
 
     my_meta_model = metamodel_from_file(
-        join(abspath(dirname(__file__)),
-             'components_model1', 'Components.tx'))
-    my_meta_model.register_scope_providers({
-        "*.*": scoping_providers.FQN(),
-        "Connection.from_port":
-            scoping_providers.ExtRelativeName("from_inst.component",
-                                              "slots",
-                                              "extends"),
-        "Connection.to_port":
-            scoping_providers.ExtRelativeName("to_inst.component",
-                                              "slots",
-                                              "extends"),
-    })
+        join(abspath(dirname(__file__)), "components_model1", "Components.tx")
+    )
+    my_meta_model.register_scope_providers(
+        {
+            "*.*": scoping_providers.FQN(),
+            "Connection.from_port": scoping_providers.ExtRelativeName(
+                "from_inst.component", "slots", "extends"
+            ),
+            "Connection.to_port": scoping_providers.ExtRelativeName(
+                "to_inst.component", "slots", "extends"
+            ),
+        }
+    )
 
     #################################
     # MODEL PARSING
     #################################
 
     my_model1 = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "components_model1", "example_inherit1.components"))
+        join(
+            abspath(dirname(__file__)), "components_model1", "example_inherit1.components"
+        )
+    )
     my_model2 = my_meta_model.model_from_file(
-        join(abspath(dirname(__file__)),
-             "components_model1", "example_inherit2.components"))
+        join(
+            abspath(dirname(__file__)), "components_model1", "example_inherit2.components"
+        )
+    )
 
     #################################
     # TEST MODEL
diff --git a/tests/functional/test_special_model_attributes.py b/tests/functional/test_special_model_attributes.py
index ae626ff4c..64220b6ac 100644
--- a/tests/functional/test_special_model_attributes.py
+++ b/tests/functional/test_special_model_attributes.py
@@ -1,32 +1,31 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 import os
 from textx import metamodel_from_str
 
 
-metamodel_str = '''
+metamodel_str = """
     Root:
         objs+=MyObj[',']
     ;
     MyObj:
         val=INT
     ;
-'''
+"""
 
 
 def test_special_model_attributes():
-
     this_folder = os.path.abspath(os.path.dirname(__file__))
     mm = metamodel_from_str(metamodel_str)
     model = mm.model_from_file(
-        os.path.join(this_folder, 'test_special_model_attributes.model'))
+        os.path.join(this_folder, "test_special_model_attributes.model")
+    )
 
     assert model._tx_filename
-    assert model._tx_filename.endswith('test_special_model_attributes.model')
+    assert model._tx_filename.endswith("test_special_model_attributes.model")
     assert model._tx_metamodel is mm
     assert model.objs[1]._tx_position == 4
 
-    model = mm.model_from_str('34, 56')
+    model = mm.model_from_str("34, 56")
 
     assert model._tx_filename is None
     assert model._tx_metamodel is mm
diff --git a/tests/functional/test_strict_unicode.py b/tests/functional/test_strict_unicode.py
index 2154b1ada..a2d3c64a7 100644
--- a/tests/functional/test_strict_unicode.py
+++ b/tests/functional/test_strict_unicode.py
@@ -1,19 +1,15 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 from textx import metamodel_from_str, TextXError
 
 
-def test_that_passing_a_non_unicode_raises_exception():
-
+def test_that_passing_a_non_string_raises_exception():
     # Test metamodel construction
-    with pytest.raises(TextXError,
-                       match=r'textX accepts only unicode strings.'):
+    with pytest.raises(TextXError, match=r"textX accepts only strings."):
         metamodel = metamodel_from_str(42)
 
-    metamodel = metamodel_from_str('First: INT;')
-    metamodel.model_from_str('42')
+    metamodel = metamodel_from_str("First: INT;")
+    metamodel.model_from_str("42")
 
     # Test model constuction
-    with pytest.raises(TextXError,
-                       match=r'textX accepts only unicode strings.'):
+    with pytest.raises(TextXError, match=r"textX accepts only strings."):
         metamodel.model_from_str(42)
diff --git a/tests/functional/test_textx_language.py b/tests/functional/test_textx_language.py
index 777e80524..7b81b3ed5 100644
--- a/tests/functional/test_textx_language.py
+++ b/tests/functional/test_textx_language.py
@@ -1,33 +1,24 @@
-from __future__ import unicode_literals
-import sys
 import pytest
 
-from textx import metamodel_from_str, TextXSyntaxError
+from textx import TextXSyntaxError, metamodel_from_str
+from textx.const import RULE_ABSTRACT, RULE_COMMON, RULE_MATCH
 from textx.lang import ALL_TYPE_NAMES
-from textx.const import RULE_MATCH, RULE_ABSTRACT, RULE_COMMON
-
-if sys.version < '3':
-    text = unicode  # noqa
-else:
-    text = str
 
 
 def test_common_rule():
-
     grammar = """
     Model: a = 'something';
     """
     meta = metamodel_from_str(grammar)
     assert meta
 
-    model = meta.model_from_str('something')
+    model = meta.model_from_str("something")
     assert model
     assert model.__class__.__name__ == "Model"
     assert model.a == "something"
 
 
 def test_abstract_rule():
-
     grammar = """
     Model: 'start' attr=Rule;
     Rule: Rule1|Rule2|Rule3;
@@ -39,17 +30,17 @@ def test_abstract_rule():
     """
     meta = metamodel_from_str(grammar)
     assert meta
-    assert set([x.__name__ for x in meta]) == \
-        set(['Model', 'Rule', 'RuleA', 'RuleB', 'Rule1', 'Rule2', 'Rule3'])\
-        .union(set(ALL_TYPE_NAMES))
-    assert meta['Rule']._tx_type is RULE_ABSTRACT
-    assert meta['Rule1']._tx_type is RULE_ABSTRACT
+    assert set([x.__name__ for x in meta]) == set(
+        ["Model", "Rule", "RuleA", "RuleB", "Rule1", "Rule2", "Rule3"]
+    ).union(set(ALL_TYPE_NAMES))
+    assert meta["Rule"]._tx_type is RULE_ABSTRACT
+    assert meta["Rule1"]._tx_type is RULE_ABSTRACT
 
-    model = meta.model_from_str('start 34')
+    model = meta.model_from_str("start 34")
     assert model
     assert model.attr
     assert model.attr.a == 34
-    assert model.attr.__class__.__name__ == 'RuleA'
+    assert model.attr.__class__.__name__ == "RuleA"
 
 
 def test_abstract_single():
@@ -72,10 +63,10 @@ def test_abstract_single():
     metamodel = metamodel_from_str(grammar)
     metamodel.model_from_str('image("testimage.svg")')
 
-    assert metamodel['IconSpecification']._tx_type is RULE_COMMON
-    assert metamodel['Command']._tx_type is RULE_ABSTRACT
-    assert metamodel['Command']._tx_inh_by == [metamodel['ImageCommand']]
-    assert metamodel['ImageCommand']._tx_type is RULE_COMMON
+    assert metamodel["IconSpecification"]._tx_type is RULE_COMMON
+    assert metamodel["Command"]._tx_type is RULE_ABSTRACT
+    assert metamodel["Command"]._tx_inh_by == [metamodel["ImageCommand"]]
+    assert metamodel["ImageCommand"]._tx_type is RULE_COMMON
 
 
 def test_abstract_with_match_rule():
@@ -88,7 +79,7 @@ def test_abstract_with_match_rule():
     Rule1: a='a'; // common rule
     """
     meta = metamodel_from_str(grammar)
-    assert meta['Rule']._tx_type is RULE_ABSTRACT
+    assert meta["Rule"]._tx_type is RULE_ABSTRACT
 
 
 def test_unreferenced_abstract_rule():
@@ -102,10 +93,10 @@ def test_unreferenced_abstract_rule():
     """
     mm = metamodel_from_str(grammar)
 
-    assert mm['First']._tx_type == RULE_COMMON
-    assert mm['Second']._tx_type == RULE_ABSTRACT
-    assert mm['Second']._tx_inh_by == [mm['Third']]
-    assert mm['Third']._tx_type == RULE_COMMON
+    assert mm["First"]._tx_type == RULE_COMMON
+    assert mm["Second"]._tx_type == RULE_ABSTRACT
+    assert mm["Second"]._tx_inh_by == [mm["Third"]]
+    assert mm["Third"]._tx_type == RULE_COMMON
 
 
 def test_abstract_rule_with_multiple_match_rule_refs():
@@ -120,14 +111,14 @@ def test_abstract_rule_with_multiple_match_rule_refs():
     Sufix: '--';
     """
     meta = metamodel_from_str(grammar)
-    assert meta['Rule']._tx_type is RULE_ABSTRACT
+    assert meta["Rule"]._tx_type is RULE_ABSTRACT
 
-    model = meta.model_from_str('# 23 --')
-    assert model == '#23--'
+    model = meta.model_from_str("# 23 --")
+    assert model == "#23--"
 
-    model = meta.model_from_str('a')
-    assert type(model).__name__ == 'Rule1'
-    assert model.a == 'a'
+    model = meta.model_from_str("a")
+    assert type(model).__name__ == "Rule1"
+    assert model.a == "a"
 
 
 def test_abstract_rule_with_multiple_rule_refs():
@@ -142,8 +133,8 @@ def test_abstract_rule_with_multiple_rule_refs():
     Sufix: '--';
     """
     meta = metamodel_from_str(grammar)
-    model = meta.model_from_str('# 23 --')
-    assert meta['Rule']._tx_type is RULE_ABSTRACT
+    model = meta.model_from_str("# 23 --")
+    assert meta["Rule"]._tx_type is RULE_ABSTRACT
     assert model.a == 23
 
 
@@ -161,15 +152,14 @@ def test_abstract_rule_with_multiple_common_rule_refs():
     """  # noqa
     meta = metamodel_from_str(grammar)
     model = meta.model_from_str('# 23 -- "some string"')
-    assert meta['Rule']._tx_type is RULE_ABSTRACT
+    assert meta["Rule"]._tx_type is RULE_ABSTRACT
     # Only Rule1 is returned
-    assert model.__class__.__name__ == 'Rule1'
+    assert model.__class__.__name__ == "Rule1"
     assert model.a == 23
 
 
 def test_abstract_rule_with_sequence_top_rule():
-    """
-    """
+    """ """
     grammar = """
     Rule: (STRING|Rule1|ID|'#' Rule1) Sufix;
     Rule1: a=INT; // common rule
@@ -177,14 +167,13 @@ def test_abstract_rule_with_sequence_top_rule():
     Sufix: '--';
     """
     meta = metamodel_from_str(grammar)
-    model = meta.model_from_str('# 23 --')
-    assert meta['Rule']._tx_type is RULE_ABSTRACT
+    model = meta.model_from_str("# 23 --")
+    assert meta["Rule"]._tx_type is RULE_ABSTRACT
     assert model.a == 23
 
 
 def test_abstract_rule_with_multiple_references_and_complex_nesting():
-    """
-    """
+    """ """
     grammar = """
     Rule: STRING|ID|'#' Rule1 Sufix;
     Rule1: a=INT; // common rule
@@ -193,8 +182,8 @@ def test_abstract_rule_with_multiple_references_and_complex_nesting():
     SomeOtherSufix: '--' '#';
     """
     meta = metamodel_from_str(grammar)
-    model = meta.model_from_str('# 23 --  #')
-    assert meta['Rule']._tx_type is RULE_ABSTRACT
+    model = meta.model_from_str("# 23 --  #")
+    assert meta["Rule"]._tx_type is RULE_ABSTRACT
     assert model.a == 23
 
 
@@ -208,11 +197,11 @@ def test_match_rule():
     """
     meta = metamodel_from_str(grammar)
     assert meta
-    assert meta['Rule']._tx_type is RULE_MATCH
+    assert meta["Rule"]._tx_type is RULE_MATCH
 
-    model = meta.model_from_str('two')
+    model = meta.model_from_str("two")
     assert model
-    assert model.__class__ == text
+    assert model.__class__ == str
     assert model == "two"
 
 
@@ -226,11 +215,11 @@ def test_match_rule_multiple():
     """
     meta = metamodel_from_str(grammar)
     assert meta
-    assert meta['Rule']._tx_type is RULE_MATCH
+    assert meta["Rule"]._tx_type is RULE_MATCH
 
-    model = meta.model_from_str(' one two')
+    model = meta.model_from_str(" one two")
     assert model
-    assert model.__class__ == text
+    assert model.__class__ == str
     assert model == "onetwo"
 
 
@@ -243,11 +232,11 @@ def test_match_rule_complex():
     """
     meta = metamodel_from_str(grammar)
     assert meta
-    assert meta['Rule']._tx_type is RULE_MATCH
+    assert meta["Rule"]._tx_type is RULE_MATCH
 
-    model = meta.model_from_str('one 45 one 78 foo foo foo')
+    model = meta.model_from_str("one 45 one 78 foo foo foo")
     assert model
-    assert model.__class__ == text
+    assert model.__class__ == str
     assert model == "one45one78foofoofoo"
 
 
@@ -267,10 +256,12 @@ def test_match_rule_suppress():
     """
 
     meta = metamodel_from_str(grammar)
-    model = meta.model_from_str('''
+    model = meta.model_from_str(
+        """
                                 first."second".third."fourth"
-                                ''')
-    assert model == 'first.second.third.fourth'
+                                """
+    )
+    assert model == "first.second.third.fourth"
 
     # Checking suppress rule reference
     grammar = """
@@ -279,9 +270,9 @@ def test_match_rule_suppress():
         Third: Second;
     """
     meta = metamodel_from_str(grammar)
-    model = meta.model_from_str('a b b')
+    model = meta.model_from_str("a b b")
     # Second b should be suppressed
-    assert model == 'ab'
+    assert model == "ab"
 
 
 def test_rule_single_reference_to_match_rule():
@@ -295,9 +286,9 @@ def test_rule_single_reference_to_match_rule():
     """
     mm = metamodel_from_str(grammar)
 
-    assert mm['First']._tx_type == RULE_COMMON
-    assert mm['Second']._tx_type == RULE_MATCH
-    assert mm['Third']._tx_type == RULE_MATCH
+    assert mm["First"]._tx_type == RULE_COMMON
+    assert mm["Second"]._tx_type == RULE_MATCH
+    assert mm["Third"]._tx_type == RULE_MATCH
 
 
 def test_regex_match_rule():
@@ -310,13 +301,12 @@ def test_regex_match_rule():
     """
     meta = metamodel_from_str(grammar)
     assert meta
-    assert meta['Rule']._tx_type is RULE_MATCH
-    assert set([x.__name__ for x in meta]) == set(['Rule'])\
-        .union(set(ALL_TYPE_NAMES))
+    assert meta["Rule"]._tx_type is RULE_MATCH
+    assert set([x.__name__ for x in meta]) == set(["Rule"]).union(set(ALL_TYPE_NAMES))
 
-    model = meta.model_from_str('bar7')
+    model = meta.model_from_str("bar7")
     assert model
-    assert model.__class__ == text
+    assert model.__class__ == str
     assert model == "bar7"
 
 
@@ -329,9 +319,9 @@ def test_basetype_match_rule_is_match():
     """
     meta = metamodel_from_str(grammar)
     assert meta
-    assert meta['Rule']._tx_type is RULE_MATCH
+    assert meta["Rule"]._tx_type is RULE_MATCH
 
-    model = meta.model_from_str('34')
+    model = meta.model_from_str("34")
     assert model
     assert model.__class__ == int
     assert model == 34
@@ -348,9 +338,9 @@ def test_simple_match_basetype_is_match_rule():
     """
     meta = metamodel_from_str(grammar)
     assert meta
-    assert meta['Rule']._tx_type is RULE_MATCH
+    assert meta["Rule"]._tx_type is RULE_MATCH
 
-    model = meta.model_from_str('34')
+    model = meta.model_from_str("34")
     assert model
     assert model.__class__ == int
     assert model == 34
@@ -373,16 +363,17 @@ def test_all_basetypes():
     """
     meta = metamodel_from_str(grammar)
     assert meta
-    assert meta['Rule']._tx_type is RULE_COMMON
-    assert meta['BASETYPE']._tx_type is RULE_MATCH
-    assert meta['NUMBER']._tx_type is RULE_MATCH
-    assert meta['INT']._tx_type is RULE_MATCH
-    assert meta['FLOAT']._tx_type is RULE_MATCH
-    assert meta['STRING']._tx_type is RULE_MATCH
-    assert meta['BOOL']._tx_type is RULE_MATCH
-
-    model = meta.model_from_str('3.4 5 true 0 "some string" '
-                                '\'some other string\' some_id')
+    assert meta["Rule"]._tx_type is RULE_COMMON
+    assert meta["BASETYPE"]._tx_type is RULE_MATCH
+    assert meta["NUMBER"]._tx_type is RULE_MATCH
+    assert meta["INT"]._tx_type is RULE_MATCH
+    assert meta["FLOAT"]._tx_type is RULE_MATCH
+    assert meta["STRING"]._tx_type is RULE_MATCH
+    assert meta["BOOL"]._tx_type is RULE_MATCH
+
+    model = meta.model_from_str(
+        '3.4 5 true 0 "some string" ' "'some other string' some_id"
+    )
     assert model.a == 3.4
     assert model.b == 5
     assert model.c1 is True
@@ -411,7 +402,7 @@ def test_basetype():
 
     meta = metamodel_from_str(grammar)
     assert meta
-    assert meta['Rule']._tx_type is RULE_COMMON
+    assert meta["Rule"]._tx_type is RULE_COMMON
 
     model = meta.model_from_str('False false true True 0 4.5 "string" some_id')
 
@@ -438,19 +429,19 @@ def test_float_int_number():
     """
     meta = metamodel_from_str(grammar)
 
-    model = meta.model_from_str('3.4 5 .3')
+    model = meta.model_from_str("3.4 5 .3")
     assert model.a == 3.4
-    assert type(model.a) is float
+    assert isinstance(model.a, float)
     assert model.b == 5
     assert model.c == 0.3
 
-    model = meta.model_from_str('3 5 2.0')
+    model = meta.model_from_str("3 5 2.0")
     assert model.a == 3
-    assert type(model.a) is int
+    assert isinstance(model.a, int)
 
     assert model.b == 5
     assert model.c == 2
-    assert type(model.c) is float
+    assert isinstance(model.c, float)
 
 
 def test_float_variations():
@@ -463,16 +454,16 @@ def test_float_variations():
     """
     meta = metamodel_from_str(grammar)
 
-    model = meta.model_from_str('3.5, .4, 5.0, 6., 7i')
+    model = meta.model_from_str("3.5, .4, 5.0, 6., 7i")
     assert len(model.a) == 4
-    assert type(model.a[0]) is float
-    assert type(model.a[1]) is float
-    assert type(model.a[2]) is float
-    assert type(model.a[3]) is float
-    assert model.some_id == '7i'
+    assert isinstance(model.a[0], float)
+    assert isinstance(model.a[1], float)
+    assert isinstance(model.a[2], float)
+    assert isinstance(model.a[3], float)
+    assert model.some_id == "7i"
 
     # Check scientific notation
-    model = meta.model_from_str('1e-2, 7i')
+    model = meta.model_from_str("1e-2, 7i")
     assert model.a[0] == 0.01
 
 
@@ -485,12 +476,14 @@ def test_string_escaping():
     """
     meta = metamodel_from_str(grammar)
 
-    model = meta.model_from_str(r"""
+    model = meta.model_from_str(
+        r"""
     "Double quotes string", "Double quotes with 'single quotes embedded'",
     "Double quotes with \" escaped quotes",
     'Single quotes string', 'Single quotes with "double quotes embedded"',
     'Single quotes with \' escaped single quotes'
-    """)
+    """
+    )
 
     assert model.a[0] == r"Double quotes string"
     assert model.a[1] == r"Double quotes with 'single quotes embedded'"
@@ -501,7 +494,6 @@ def test_string_escaping():
 
 
 def test_rule_call_forward_backward_reference():
-
     grammar = """
     Model: 'start' attr=Rule2;
     Rule1: 'one'|'two'|'three';
@@ -509,10 +501,11 @@ def test_rule_call_forward_backward_reference():
     """
     meta = metamodel_from_str(grammar)
     assert meta
-    assert set([x.__name__ for x in meta]) == set(['Model', 'Rule1', 'Rule2'])\
-        .union(set(ALL_TYPE_NAMES))
+    assert set([x.__name__ for x in meta]) == set(["Model", "Rule1", "Rule2"]).union(
+        set(ALL_TYPE_NAMES)
+    )
 
-    model = meta.model_from_str('start rule2 three')
+    model = meta.model_from_str("start rule2 three")
     assert model
     assert model.attr
     assert model.attr.attr
@@ -520,7 +513,6 @@ def test_rule_call_forward_backward_reference():
 
 
 def test_assignment_zeroormore():
-
     grammar = """
     Model: 'start' attr*=Rule;     // There should be zero or more Rule-s after
                                     // 'start'
@@ -531,19 +523,19 @@ def test_assignment_zeroormore():
     """
     meta = metamodel_from_str(grammar)
     assert meta
-    assert set([x.__name__ for x in meta]) == \
-        set(['Model', 'Rule', 'Rule1', 'Rule2', 'Rule3'])\
-        .union(set(ALL_TYPE_NAMES))
+    assert set([x.__name__ for x in meta]) == set(
+        ["Model", "Rule", "Rule1", "Rule2", "Rule3"]
+    ).union(set(ALL_TYPE_NAMES))
 
     model = meta.model_from_str('start 34 "foo"')
     assert model
     assert model.attr
     assert model.attr[0].a == 34
     assert model.attr[1].b == "foo"
-    assert model.attr[0].__class__.__name__ == 'Rule1'
-    assert model.attr[1].__class__.__name__ == 'Rule2'
+    assert model.attr[0].__class__.__name__ == "Rule1"
+    assert model.attr[1].__class__.__name__ == "Rule2"
 
-    model = meta.model_from_str('start')
+    model = meta.model_from_str("start")
     assert model
 
 
@@ -558,19 +550,19 @@ def test_assignment_multiple_simple():
     """
     meta = metamodel_from_str(grammar)
 
-    model = meta.model_from_str('start 34 23 45')
+    model = meta.model_from_str("start 34 23 45")
 
-    assert meta['Model']._tx_attrs['a'].cls.__name__ == 'INT'
-    assert meta['Model']._tx_attrs['a'].mult == '1..*'
-    assert meta['Model']._tx_attrs['a'].cont
-    assert not meta['Model']._tx_attrs['a'].ref
+    assert meta["Model"]._tx_attrs["a"].cls.__name__ == "INT"
+    assert meta["Model"]._tx_attrs["a"].mult == "1..*"
+    assert meta["Model"]._tx_attrs["a"].cont
+    assert not meta["Model"]._tx_attrs["a"].ref
     assert model
     assert model.a
-    assert type(model.a) is list
+    assert isinstance(model.a, list)
     assert len(model.a) == 3
     assert model.a == [34, 23, 45]
 
-    model = meta.model_from_str('start 34 23')
+    model = meta.model_from_str("start 34 23")
     assert model.a == [34, 23]
 
 
@@ -590,16 +582,15 @@ def test_assignment_multiple_different():
 
     """
     mm = metamodel_from_str(grammar)
-    assert mm['Element']._tx_attrs['name'].cls.__name__ == 'ID'
-    assert mm['Element']._tx_attrs['name'].mult == '1'
-    assert not mm['Element']._tx_attrs['name'].ref
-    assert mm['Element']._tx_attrs['value'].cls.__name__ == 'OBJECT'
-    assert mm['Element']._tx_attrs['value'].mult == '1..*'
-    assert mm['Element']._tx_attrs['value'].ref
+    assert mm["Element"]._tx_attrs["name"].cls.__name__ == "ID"
+    assert mm["Element"]._tx_attrs["name"].mult == "1"
+    assert not mm["Element"]._tx_attrs["name"].ref
+    assert mm["Element"]._tx_attrs["value"].cls.__name__ == "OBJECT"
+    assert mm["Element"]._tx_attrs["value"].mult == "1..*"
+    assert mm["Element"]._tx_attrs["value"].ref
 
 
 def test_assignment_oneoormore():
-
     grammar = """
     Model: 'start' attr+=Rule;    // There should be at least one Rule
                                  // after 'start'
@@ -610,21 +601,21 @@ def test_assignment_oneoormore():
     """
     meta = metamodel_from_str(grammar)
     assert meta
-    assert set([x.__name__ for x in meta]) == \
-        set(['Model', 'Rule', 'Rule1', 'Rule2', 'Rule3'])\
-        .union(set(ALL_TYPE_NAMES))
+    assert set([x.__name__ for x in meta]) == set(
+        ["Model", "Rule", "Rule1", "Rule2", "Rule3"]
+    ).union(set(ALL_TYPE_NAMES))
 
     model = meta.model_from_str('start 34 "foo"')
     assert model
     assert model.attr
     assert model.attr[0].a == 34
     assert model.attr[1].b == "foo"
-    assert model.attr[0].__class__.__name__ == 'Rule1'
-    assert model.attr[1].__class__.__name__ == 'Rule2'
+    assert model.attr[0].__class__.__name__ == "Rule1"
+    assert model.attr[1].__class__.__name__ == "Rule2"
 
     # There must be at least one Rule matched after 'start'
     with pytest.raises(TextXSyntaxError):
-        model = meta.model_from_str('start')
+        model = meta.model_from_str("start")
     assert model
 
 
@@ -639,17 +630,17 @@ def test_assignment_optional():
     """
     meta = metamodel_from_str(grammar)
     assert meta
-    assert set([x.__name__ for x in meta]) == \
-        set(['Model', 'Rule', 'Rule1', 'Rule2', 'Rule3'])\
-        .union(set(ALL_TYPE_NAMES))
+    assert set([x.__name__ for x in meta]) == set(
+        ["Model", "Rule", "Rule1", "Rule2", "Rule3"]
+    ).union(set(ALL_TYPE_NAMES))
 
-    model = meta.model_from_str('start')
+    model = meta.model_from_str("start")
     assert model
-    model = meta.model_from_str('start 34')
+    model = meta.model_from_str("start 34")
     assert model
     assert model.attr
     assert model.attr.a == 34
-    assert model.attr.__class__.__name__ == 'Rule1'
+    assert model.attr.__class__.__name__ == "Rule1"
 
     # There must be at most one Rule matched after 'start'
     with pytest.raises(TextXSyntaxError):
@@ -672,9 +663,9 @@ def test_repetition_separator_modifier():
     """
     meta = metamodel_from_str(grammar)
     assert meta
-    assert set([x.__name__ for x in meta]) == set(['Model', 'Rule', 'Rule1',
-                                                   'Rule2', 'Rule3'])\
-        .union(set(ALL_TYPE_NAMES))
+    assert set([x.__name__ for x in meta]) == set(
+        ["Model", "Rule", "Rule1", "Rule2", "Rule3"]
+    ).union(set(ALL_TYPE_NAMES))
 
     model = meta.model_from_str('start 34, "foo"; ident')
     assert model
@@ -682,13 +673,13 @@ def test_repetition_separator_modifier():
     assert model.attr[0].a == 34
     assert model.attr[1].b == "foo"
     assert model.attr[2].c == "ident"
-    assert model.attr[0].__class__.__name__ == 'Rule1'
-    assert model.attr[1].__class__.__name__ == 'Rule2'
-    assert model.attr[2].__class__.__name__ == 'Rule3'
+    assert model.attr[0].__class__.__name__ == "Rule1"
+    assert model.attr[1].__class__.__name__ == "Rule2"
+    assert model.attr[2].__class__.__name__ == "Rule3"
 
     # There must be at least one Rule matched after 'start'
     with pytest.raises(TextXSyntaxError):
-        model = meta.model_from_str('start')
+        model = meta.model_from_str("start")
     assert model
 
 
@@ -702,22 +693,22 @@ def test_bool_match():
     """
     meta = metamodel_from_str(grammar)
     assert meta
-    assert set([x.__name__ for x in meta]) == \
-        set(['Model', 'Rule', 'Rule1', 'Rule2', 'Rule3'])\
-        .union(set(ALL_TYPE_NAMES))
+    assert set([x.__name__ for x in meta]) == set(
+        ["Model", "Rule", "Rule1", "Rule2", "Rule3"]
+    ).union(set(ALL_TYPE_NAMES))
 
-    model = meta.model_from_str('start rule 34')
+    model = meta.model_from_str("start rule 34")
     assert model
-    assert hasattr(model, 'rule')
-    assert hasattr(model, 'rule2')
+    assert hasattr(model, "rule")
+    assert hasattr(model, "rule2")
     assert model.rule is True
     assert model.rule2 is True
 
-    model = meta.model_from_str('start 34')
+    model = meta.model_from_str("start 34")
     assert model.rule is False
     assert model.rule2 is True
 
-    model = meta.model_from_str('start')
+    model = meta.model_from_str("start")
     assert model.rule is False
     assert model.rule2 is False
 
@@ -729,14 +720,14 @@ def test_object_and_rule_reference():
     """
     meta = metamodel_from_str(grammar)
     assert meta
-    assert set([x.__name__ for x in meta]) == \
-        set(['Model', 'RuleA'])\
-        .union(set(ALL_TYPE_NAMES))
+    assert set([x.__name__ for x in meta]) == set(["Model", "RuleA"]).union(
+        set(ALL_TYPE_NAMES)
+    )
 
-    model = meta.model_from_str('start rule rule1 rule rule2 ref rule1')
+    model = meta.model_from_str("start rule rule1 rule rule2 ref rule1")
     assert model
-    assert hasattr(model, 'rules')
-    assert hasattr(model, 'ref')
+    assert hasattr(model, "rules")
+    assert hasattr(model, "ref")
     assert model.rules
     assert model.ref
 
@@ -756,14 +747,14 @@ def test_abstract_rule_and_object_reference():
     """
     meta = metamodel_from_str(grammar)
     assert meta
-    assert set([x.__name__ for x in meta]) == \
-        set(['Model', 'RuleA', 'Rule1', 'Rule2', 'RuleI', 'RuleE'])\
-        .union(set(ALL_TYPE_NAMES))
+    assert set([x.__name__ for x in meta]) == set(
+        ["Model", "RuleA", "Rule1", "Rule2", "RuleI", "RuleE"]
+    ).union(set(ALL_TYPE_NAMES))
 
-    model = meta.model_from_str('start r2 rule1 rE rule2 ref rule2')
+    model = meta.model_from_str("start r2 rule1 rE rule2 ref rule2")
     assert model
-    assert hasattr(model, 'rules')
-    assert hasattr(model, 'ref')
+    assert hasattr(model, "rules")
+    assert hasattr(model, "ref")
     assert model.rules
     assert model.ref
 
@@ -778,9 +769,9 @@ def test_repeat_rule_ref():
     """
     metamodel = metamodel_from_str(grammar)
 
-    assert metamodel['Rule']._tx_type is RULE_MATCH
+    assert metamodel["Rule"]._tx_type is RULE_MATCH
     model = metamodel.model_from_str("""first second third""")
-    assert model == 'firstsecondthird'
+    assert model == "firstsecondthird"
 
 
 def test_repeat_strmatch():
@@ -790,8 +781,8 @@ def test_repeat_strmatch():
     metamodel = metamodel_from_str(grammar)
 
     model = metamodel.model_from_str("""first first""")
-    assert metamodel['Rule']._tx_type is RULE_MATCH
-    assert model == 'firstfirst'
+    assert metamodel["Rule"]._tx_type is RULE_MATCH
+    assert model == "firstfirst"
 
 
 def test_repeat_strmatch_with_separator():
@@ -800,9 +791,9 @@ def test_repeat_strmatch_with_separator():
     """
     metamodel = metamodel_from_str(grammar)
 
-    assert metamodel['Rule']._tx_type is RULE_MATCH
+    assert metamodel["Rule"]._tx_type is RULE_MATCH
     model = metamodel.model_from_str("""first, first""")
-    assert model == 'first,first'
+    assert model == "first,first"
 
 
 def test_empty_strmatch():
@@ -813,7 +804,7 @@ def test_empty_strmatch():
     Rule: first='' 'a';
     """
     mm = metamodel_from_str(grammar)
-    model = mm.model_from_str('a')
+    model = mm.model_from_str("a")
     assert model
 
 
@@ -827,7 +818,7 @@ def test_empty_regexmatch():
     Rule: first=/()/ 'a';
     """
     mm = metamodel_from_str(grammar)
-    model = mm.model_from_str('a')
+    model = mm.model_from_str("a")
     assert model
 
     grammar = """
@@ -860,24 +851,26 @@ def test_default_attribute_values():
     """
     metamodel = metamodel_from_str(grammar)
 
-    model = metamodel.model_from_str("""
+    model = metamodel.model_from_str(
+        """
             first 45 "foo" 78
-    """)
+    """
+    )
     assert type(model).__name__ == "First"
-    assert type(model.seconds) is list
-    assert type(model.a) is int
+    assert isinstance(model.seconds, list)
+    assert isinstance(model.a, int)
     assert model.a == 0
-    assert type(model.b) is bool
+    assert isinstance(model.b, bool)
     assert model.b is False
-    assert type(model.c) is text
+    assert isinstance(model.c, str)
     assert model.c == ""
-    assert type(model.d) is float
+    assert isinstance(model.d, float)
     assert model.d == 0.0
-    assert type(model.e) is list
+    assert isinstance(model.e, list)
     assert model.e == []
-    assert type(model.f) is list
+    assert isinstance(model.f, list)
     assert model.f == []
-    assert type(model.g) is bool
+    assert isinstance(model.g, bool)
     assert model.g is False
 
 
@@ -897,21 +890,21 @@ def test_sequence_ordered_choice():
     """
     meta = metamodel_from_str(grammar, debug=True)
     assert meta
-    assert set([x.__name__ for x in meta]) == \
-        set(['Model', 'RuleA'])\
-        .union(set(ALL_TYPE_NAMES))
+    assert set([x.__name__ for x in meta]) == set(["Model", "RuleA"]).union(
+        set(ALL_TYPE_NAMES)
+    )
 
-    model = meta.model_from_str('first 23 a_is_here END')
+    model = meta.model_from_str("first 23 a_is_here END")
     assert model.a == 23
     assert model.c == 0
     assert model.b is True
     assert model.d is False
-    model = meta.model_from_str('second 32 END')
+    model = meta.model_from_str("second 32 END")
     assert model.a == 0
     assert model.c == 32
     assert model.b is False
     assert model.d is False
-    model = meta.model_from_str('rule A END')
+    model = meta.model_from_str("rule A END")
     assert model.a == 0
     assert model.c == 0
     assert model.b is False
@@ -935,14 +928,16 @@ def test_syntactic_predicate_not():
     """
     meta = metamodel_from_str(grammar)
 
-    model = meta.model_from_str("""
+    model = meta.model_from_str(
+        """
                                 let let let 34 end let foo end end end
-                                """)
+                                """
+    )
 
     assert model
     assert len(model.expr) == 1
     assert model.expr[0].expr[0].expr[0] == 34
-    assert model.expr[0].expr[1].expr[0] == 'foo'
+    assert model.expr[0].expr[1].expr[0] == "foo"
 
 
 def test_syntactic_predicate_and():
@@ -958,11 +953,11 @@ def test_syntactic_predicate_and():
     """
     meta = metamodel_from_str(grammar)
 
-    model = meta.model_from_str('a a a b')
+    model = meta.model_from_str("a a a b")
 
     assert model
     assert len(model.elements) == 4
-    assert model.elements[0].__class__.__name__ == 'A'
-    assert model.elements[1].__class__.__name__ == 'A'
-    assert model.elements[2].__class__.__name__ == 'AbeforeB'
-    assert model.elements[3].__class__.__name__ == 'B'
+    assert model.elements[0].__class__.__name__ == "A"
+    assert model.elements[1].__class__.__name__ == "A"
+    assert model.elements[2].__class__.__name__ == "AbeforeB"
+    assert model.elements[3].__class__.__name__ == "B"
diff --git a/tests/functional/test_textx_tools_support.py b/tests/functional/test_textx_tools_support.py
index 9e2d0706e..127669727 100644
--- a/tests/functional/test_textx_tools_support.py
+++ b/tests/functional/test_textx_tools_support.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 from textx import metamodel_from_str
 
 grammar = """
@@ -42,20 +41,19 @@ def test_textx_tools_support():
     # textx_tools_support disabled (default)
     mm = metamodel_from_str(grammar)
     model = mm.model_from_str(modelstr)
-    assert not hasattr(model, '_pos_crossref_list')
-    assert not hasattr(model, '_pos_rule_dict')
+    assert not hasattr(model, "_pos_crossref_list")
+    assert not hasattr(model, "_pos_rule_dict")
 
     # textx_tools_support enabled
     mm = metamodel_from_str(grammar, textx_tools_support=True)
     model = mm.model_from_str(modelstr)
     # check additional properties
-    assert hasattr(model, '_pos_crossref_list')
-    assert hasattr(model, '_pos_rule_dict')
+    assert hasattr(model, "_pos_crossref_list")
+    assert hasattr(model, "_pos_rule_dict")
     # check number of crossrefs
     assert len(model._pos_crossref_list) == 4
     # check rule dictionary key order
     rules_keys = list(model._pos_rule_dict)
     rules_len = len(rules_keys)
     assert rules_len > 0
-    assert rules_keys[0][0] > \
-        rules_keys[rules_len - 1][0]
+    assert rules_keys[0][0] > rules_keys[rules_len - 1][0]
diff --git a/tests/functional/test_unicode.py b/tests/functional/test_unicode.py
index a30402126..db8d05d6e 100644
--- a/tests/functional/test_unicode.py
+++ b/tests/functional/test_unicode.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import pytest  # noqa
 from textx import metamodel_from_str
 
diff --git a/tests/functional/test_user_class_attrs_frozen.py b/tests/functional/test_user_class_attrs_frozen.py
index a759f45cd..03d7c5616 100644
--- a/tests/functional/test_user_class_attrs_frozen.py
+++ b/tests/functional/test_user_class_attrs_frozen.py
@@ -1,11 +1,10 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 from textx import metamodel_from_str
 
 
-@pytest.mark.parametrize('frozen', [False, True])
+@pytest.mark.parametrize("frozen", [False, True])
 def test_user_class_attrs(frozen):
-    attr = pytest.importorskip('attr')
+    attr = pytest.importorskip("attr")
     grammar = """
     Document:
         a=A
@@ -25,23 +24,22 @@ def test_user_class_attrs(frozen):
     """
 
     @attr.s()
-    class A(object):
+    class A:
         parent = attr.ib()
         name = attr.ib()
 
     @attr.s(frozen=frozen)
-    class B(object):
+    class B:
         parent = attr.ib()
         a = attr.ib()
 
-    mm = metamodel_from_str(grammar, classes=[A, B],
-                            auto_init_attributes=False)
+    mm = metamodel_from_str(grammar, classes=[A, B], auto_init_attributes=False)
     model = mm.model_from_str(modelstr)
     assert model.b.a == model.a
 
 
 def test_inheritance_attrs():
-    attr = pytest.importorskip('attr')
+    attr = pytest.importorskip("attr")
 
     grammar = """
     Document:
@@ -62,7 +60,7 @@ def test_inheritance_attrs():
     """
 
     @attr.s()
-    class Super(object):
+    class Super:
         parent = attr.ib()
         a = attr.ib()
 
@@ -75,7 +73,7 @@ class Sub(Super):
 
     mm = metamodel_from_str(grammar, classes=[Super, Sub])
     model = mm.model_from_str(modelstr)
-    assert model.sub.b == 'something'
+    assert model.sub.b == "something"
 
     # Make sure that the special methods of both classes have been correctly
     # restored
diff --git a/tests/functional/test_user_class_constructor_calls.py b/tests/functional/test_user_class_constructor_calls.py
index 09bc4b954..7646f8293 100644
--- a/tests/functional/test_user_class_constructor_calls.py
+++ b/tests/functional/test_user_class_constructor_calls.py
@@ -1,7 +1,6 @@
 """
 Testing user class constructor call and parent reference.
 """
-from __future__ import unicode_literals
 import pytest  # noqa
 from textx import metamodel_from_str
 from textx.scoping.providers import PlainName
@@ -43,7 +42,7 @@ def test_parent_reference():
 
     # First is not nested so it should not have a parent
     # attribute
-    assert not hasattr(model, 'parent')
+    assert not hasattr(model, "parent")
 
 
 def test_user_class_constructor_call():
@@ -52,7 +51,7 @@ def test_user_class_constructor_call():
     that parent reference is given in the parameters.
     """
 
-    class Second(object):
+    class Second:
         _called = False
 
         def __init__(self, parent, sec):
@@ -79,13 +78,13 @@ def test_user_class_pass_to_constructor_only_metamodel_defined_attrs():
     meta-model defined attributes should be passed to the user-class
     constructor.
     """
-    grammar = r'''
+    grammar = r"""
     Model: elements*=Element refs*=Reference;
     Element: 'E' name=ID;
     Reference: 'ref' ref=[Element];
-    '''
+    """
 
-    class Element(object):
+    class Element:
         _called = False
 
         def __init__(self, parent, name):
@@ -102,14 +101,12 @@ def my_scope_provider(obj, attr, obj_ref):
         return PlainName()(obj, attr, obj_ref)
 
     metamodel = metamodel_from_str(grammar, classes=[Element])
-    metamodel.register_scope_providers({
-        'Reference.ref': my_scope_provider
-    })
+    metamodel.register_scope_providers({"Reference.ref": my_scope_provider})
 
-    model_str = r'''
+    model_str = r"""
     E first E second
     ref second
-    '''
+    """
     model = metamodel.model_from_str(model_str)
 
     assert scope_provider_called[0]
diff --git a/tests/functional/test_user_class_with_slots.py b/tests/functional/test_user_class_with_slots.py
index 2a96a52c6..1ace14b4c 100644
--- a/tests/functional/test_user_class_with_slots.py
+++ b/tests/functional/test_user_class_with_slots.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 from textx import metamodel_from_str
 
@@ -18,9 +17,11 @@ def test_user_class_with_slots():
     """
     User supplied meta class.
     """
-    class Point(object):
+
+    class Point:
         "User class."
-        __slots__ = ['parent', 'x', 'y']
+
+        __slots__ = ["parent", "x", "y"]
 
         def __init__(self, parent, x, y):
             self.parent = parent
@@ -34,8 +35,7 @@ def __init__(self, parent, x, y):
     end
     """
 
-    mm = metamodel_from_str(grammar, classes=[Point],
-                            auto_init_attributes=False)
+    mm = metamodel_from_str(grammar, classes=[Point], auto_init_attributes=False)
     model = mm.model_from_str(modelstr)
     # Test that user class is instantiated
     point = model.points[0]
diff --git a/tests/functional/test_user_classes.py b/tests/functional/test_user_classes.py
index ce13ea872..66810d84f 100644
--- a/tests/functional/test_user_classes.py
+++ b/tests/functional/test_user_classes.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 from textx import metamodel_from_str, metamodel_from_file
 from os.path import join, dirname, abspath
@@ -26,8 +25,9 @@ def test_user_class():
     User supplied meta class.
     """
 
-    class First(object):
+    class First:
         "User class."
+
         def __init__(self, seconds, a, b, c):
             "Constructor must be without parameters."
             # Testing that additional attributes
@@ -40,10 +40,11 @@ def __init__(self, seconds, a, b, c):
 
             for second in self.seconds:
                 # Make sure seconds have already been instantiated
-                assert hasattr(second, 'sec') and isinstance(second.sec, int)
+                assert hasattr(second, "sec") and isinstance(second.sec, int)
 
-    class Second(object):
+    class Second:
         "User class"
+
         def __init__(self, parent, sec):
             self.parent = parent
             self.sec = sec
@@ -56,55 +57,58 @@ def __init__(self, parent, sec):
     model = mm.model_from_str(modelstr)
     # Test that generic First class is created
     assert type(model).__name__ == "First"
-    assert type(model) is not First
+    assert not isinstance(model, First)
 
     mm = metamodel_from_str(grammar, classes=[First, Second])
     model = mm.model_from_str(modelstr)
     # Test that user class is instantiated
     assert type(model).__name__ == "First"
-    assert type(model) is First
+    assert isinstance(model, First)
     # Check default attributes
-    assert type(model.a) is list
+    assert isinstance(model.a, list)
     assert model.a == []
-    assert type(model.b) is bool
+    assert isinstance(model.b, bool)
     assert model.b is False
 
     # Check additional attributes
     assert model.some_attr == 1
 
 
-class Thing(object):
+class Thing:
     def __init__(self, **kwargs):
-        for k in kwargs.keys():
+        for k in kwargs:
             setattr(self, k, kwargs[k])
 
 
-class AThing(object):
+class AThing:
     def __init__(self, **kwargs):
-        for k in kwargs.keys():
+        for k in kwargs:
             setattr(self, k, kwargs[k])
 
 
-class BThing(object):
+class BThing:
     def __init__(self, **kwargs):
-        for k in kwargs.keys():
+        for k in kwargs:
             setattr(self, k, kwargs[k])
 
 
 def test_user_class_with_imported_grammar():
     this_folder = dirname(abspath(__file__))
-    mm = metamodel_from_file(join(this_folder, "user_classes", "B.tx"),
-                             classes=[AThing, BThing])
+    mm = metamodel_from_file(
+        join(this_folder, "user_classes", "B.tx"), classes=[AThing, BThing]
+    )
     called = [False]
 
     def dummy(_):
         called[0] = True
 
-    mm.register_obj_processors({'AThing': dummy})
-    m = mm.model_from_str("""
+    mm.register_obj_processors({"AThing": dummy})
+    m = mm.model_from_str(
+        """
         A 2,1
         B Hello
-    """)
+    """
+    )
     assert called[0]
     assert m.a.v.x == 2
     assert m.a.v.y == 1
@@ -114,10 +118,13 @@ def dummy(_):
     assert isinstance(m.a, AThing)
     assert isinstance(m.b, BThing)
 
-    with raises(TextXSemanticError,
-                match=r'.*redefined imported rule Thing'
-                      + r' cannot be replaced by a user class.*'):
-        mm = metamodel_from_file(join(this_folder, "user_classes", "B.tx"),
-                                 classes=[AThing, BThing, Thing])
+    with raises(
+        TextXSemanticError,
+        match=r".*redefined imported rule Thing"
+        + r" cannot be replaced by a user class.*",
+    ):
+        mm = metamodel_from_file(
+            join(this_folder, "user_classes", "B.tx"), classes=[AThing, BThing, Thing]
+        )
     # now, all involved user classes **may** be instrumented...
     # (after an exception, we do not guarantee 100% cleanup of user classes)
diff --git a/tests/functional/test_user_classes_attr_functions.py b/tests/functional/test_user_classes_attr_functions.py
index 699972f01..8828cafab 100644
--- a/tests/functional/test_user_classes_attr_functions.py
+++ b/tests/functional/test_user_classes_attr_functions.py
@@ -1,25 +1,25 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 from textx import metamodel_from_str
 
-grammar = r'''
+grammar = r"""
 A: 'A' x=INT b=B c=C;
 B: 'B' x=INT;
 C: 'C' x=INT;
-'''
+"""
 
-modelstr = r'''
+modelstr = r"""
 A 1 B 2 C 3
-'''
+"""
 
 
-class A(object):
+class A:
     """
     A defines a class with a custom setattr method
     (it stores the __setattr__ in its __dict__).
     """
+
     def __init__(self, **kwargs):
-        super(A, self).__init__()
+        super().__init__()
         for k, v in kwargs.items():
             self.__dict__[k] = v
 
@@ -32,8 +32,9 @@ class B(A):
     B defines a class inheriting the custom setattr method from A
     (it does not store the __setattr__ in its __dict__).
     """
+
     def __init__(self, **kwargs):
-        super(B, self).__init__(**kwargs)
+        super().__init__(**kwargs)
 
 
 class C(B):
@@ -42,8 +43,9 @@ class C(B):
     It overrides the __setattr__ from B/A.
     (it stores its own __setattr__ in its __dict__).
     """
+
     def __init__(self, **kwargs):
-        super(C, self).__init__(**kwargs)
+        super().__init__(**kwargs)
 
     def __setattr__(self, name, value):
         pass
@@ -55,9 +57,9 @@ def test_user_class_attr_functions_are_restored_correctly():
     Documentation of correct handling of custom attr methods.
     """
 
-    origA = A.__dict__['__setattr__']
-    assert '__setattr__' not in B.__dict__
-    origC = C.__dict__['__setattr__']
+    origA = A.__dict__["__setattr__"]
+    assert "__setattr__" not in B.__dict__
+    origC = C.__dict__["__setattr__"]
 
     assert origA is not None
     assert origC is not None
@@ -65,13 +67,13 @@ def test_user_class_attr_functions_are_restored_correctly():
     mm = metamodel_from_str(grammar, classes=[A, B, C])
     _ = mm.model_from_str(modelstr)
 
-    assert origA is A.__dict__['__setattr__']
-    assert '__setattr__' not in B.__dict__
-    assert origC is C.__dict__['__setattr__']
+    assert origA is A.__dict__["__setattr__"]
+    assert "__setattr__" not in B.__dict__
+    assert origC is C.__dict__["__setattr__"]
 
     mm = metamodel_from_str(grammar, classes=[C, B, A])
     _ = mm.model_from_str(modelstr)
 
-    assert origA is A.__dict__['__setattr__']
-    assert '__setattr__' not in B.__dict__
-    assert origC is C.__dict__['__setattr__']
+    assert origA is A.__dict__["__setattr__"]
+    assert "__setattr__" not in B.__dict__
+    assert origC is C.__dict__["__setattr__"]
diff --git a/tests/functional/test_user_classes_callable.py b/tests/functional/test_user_classes_callable.py
index 61aa935ab..3b7b72558 100644
--- a/tests/functional/test_user_classes_callable.py
+++ b/tests/functional/test_user_classes_callable.py
@@ -1,7 +1,7 @@
-from __future__ import unicode_literals
-from textx.metamodel import metamodel_from_str
 from pytest import raises
+
 from textx.exceptions import TextXSemanticError
+from textx.metamodel import metamodel_from_str
 
 grammar = """
 MyModel: 'model' name=ID
@@ -54,9 +54,9 @@
 """
 
 
-class ConnectionHandler(object):
+class ConnectionHandler:
     def _init_(self):
-        print('')
+        print("")
 
     def awesomeMethod4SenderAndReceiver(self):
         print("I am really important for Sender and Receiver")
@@ -64,14 +64,14 @@ def awesomeMethod4SenderAndReceiver(self):
 
 class Sender(ConnectionHandler):
     def __init__(self, name=None, connection=None, parent=None):
-        super(Sender, self).__init__()
-        print('')
+        super().__init__()
+        print("")
 
 
 class Receiver(ConnectionHandler):
     def __init__(self, name=None, connection=None, parent=None):
-        super(Receiver, self).__init__()
-        print('')
+        super().__init__()
+        print("")
 
 
 def test_user_classes_callable():
@@ -84,13 +84,15 @@ def test_user_classes_callable():
     _ = mm.model_from_str(modelstring)
 
     # fix/works (no unused user classes; see grammar_with_baseclass_fix):
-    mm = metamodel_from_str(grammar_with_baseclass_fix,
-                            classes=[ConnectionHandler, Sender, Receiver])
+    mm = metamodel_from_str(
+        grammar_with_baseclass_fix, classes=[ConnectionHandler, Sender, Receiver]
+    )
     _ = mm.model_from_str(modelstring)
 
     # does not work
-    with raises(TextXSemanticError,
-                match="ConnectionHandler class is not used in the grammar"):
+    with raises(
+        TextXSemanticError, match="ConnectionHandler class is not used in the grammar"
+    ):
         _ = metamodel_from_str(grammar, classes=[ConnectionHandler, Sender, Receiver])
 
     # does work (allow unused user classes by providing a callable instead of
diff --git a/tests/functional/test_user_classes_init_order.py b/tests/functional/test_user_classes_init_order.py
index 8ebf89fcf..b60d7268d 100644
--- a/tests/functional/test_user_classes_init_order.py
+++ b/tests/functional/test_user_classes_init_order.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 from textx import metamodel_from_str
 
@@ -20,15 +19,17 @@ def test_user_class_init_order():
 
     init_order = []
 
-    class First(object):
+    class First:
         "User class."
+
         def __init__(self, seconds):
             "Constructor must be without parameters."
             self.seconds = seconds
             init_order.append(self)
 
-    class Second(object):
+    class Second:
         "User class"
+
         def __init__(self, parent, sec):
             self.parent = parent
             self.sec = sec
diff --git a/tests/functional/test_user_classes_with_properties.py b/tests/functional/test_user_classes_with_properties.py
index 3464c0565..f43dbe186 100644
--- a/tests/functional/test_user_classes_with_properties.py
+++ b/tests/functional/test_user_classes_with_properties.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 import pytest  # noqa
 from textx import metamodel_from_str
 
@@ -10,7 +9,7 @@
 """
 
 
-class UserModel(object):
+class UserModel:
     def __init__(self, a):
         self._a = a
 
diff --git a/tests/functional/textx_textx/test_textx_rrel.py b/tests/functional/textx_textx/test_textx_rrel.py
index a57acd79f..06b306e75 100644
--- a/tests/functional/textx_textx/test_textx_rrel.py
+++ b/tests/functional/textx_textx/test_textx_rrel.py
@@ -1,27 +1,29 @@
-from __future__ import unicode_literals
-from os.path import join, abspath, dirname
-from textx import metamodel_for_language, get_children_of_type
+from os.path import abspath, dirname, join
 
+from textx import get_children_of_type, metamodel_for_language
 
-def test_textx_rrel_multi():
 
-    textx_mm = metamodel_for_language('textx')
+def test_textx_rrel_multi():
+    textx_mm = metamodel_for_language("textx")
     grammar_model = textx_mm.grammar_model_from_file(
-        join(abspath(dirname(__file__)), 'textx_rrel_test.tx'))
+        join(abspath(dirname(__file__)), "textx_rrel_test.tx")
+    )
 
-    rrels = get_children_of_type('RRELExpression', grammar_model)
+    rrels = get_children_of_type("RRELExpression", grammar_model)
 
     # Multi
     assert rrels[0].multi
 
     # Multi with parent
-    assert rrels[1].multi \
-        and rrels[1].sequence.paths[0].parts[0].__class__.__name__ == 'RRELParent'
+    assert (
+        rrels[1].multi
+        and rrels[1].sequence.paths[0].parts[0].__class__.__name__ == "RRELParent"
+    )
 
     # Dots
-    dots = get_children_of_type('RRELPath', rrels[3])
-    assert any([x.dots == '..' for x in dots])
+    dots = get_children_of_type("RRELPath", rrels[3])
+    assert any([x.dots == ".." for x in dots])
 
     # Brackets
-    brackets = get_children_of_type('RRELBrackets', rrels[4])
+    brackets = get_children_of_type("RRELBrackets", rrels[4])
     assert len(brackets) == 1
diff --git a/tests/functional/textx_textx/test_textx_textx.py b/tests/functional/textx_textx/test_textx_textx.py
index 92b6ae8f6..ea77553e1 100644
--- a/tests/functional/textx_textx/test_textx_textx.py
+++ b/tests/functional/textx_textx/test_textx_textx.py
@@ -1,6 +1,6 @@
-from __future__ import unicode_literals
-from os.path import join, abspath, dirname
-from textx import metamodel_for_language, get_children_of_type
+from os.path import abspath, dirname, join
+
+from textx import get_children_of_type, metamodel_for_language
 
 
 def test_textx_textx():
@@ -8,34 +8,34 @@ def test_textx_textx():
     Test textX definition in textX. Used to analyze grammars programmatically.
     """
 
-    textx_mm = metamodel_for_language('textx')
+    textx_mm = metamodel_for_language("textx")
     grammar_model = textx_mm.grammar_model_from_file(
-        join(abspath(dirname(__file__)), 'pyflies.tx'))
+        join(abspath(dirname(__file__)), "pyflies.tx")
+    )
 
     def get_rule_by_name(name):
-        return next((x for x in grammar_model.rules if x.name == name))
+        return next(x for x in grammar_model.rules if x.name == name)
 
     # Check rules with params
-    rule_with_params = get_rule_by_name('TestType')
+    rule_with_params = get_rule_by_name("TestType")
     assert len(rule_with_params.params) == 2
     p = rule_with_params.params
-    assert p[0].name == 'noskipws'
-    assert p[1].name == 'my_param'
-    assert p[1].value == 'some value'
+    assert p[0].name == "noskipws"
+    assert p[1].name == "my_param"
+    assert p[1].value == "some value"
 
     # Check string matches in the rule
-    str_matches = get_children_of_type('StrMatch', rule_with_params)
+    str_matches = get_children_of_type("StrMatch", rule_with_params)
     assert len(str_matches) == 3
-    assert str_matches[1].match == '{'
+    assert str_matches[1].match == "{"
 
     # Check match ordered choice
-    rule = get_rule_by_name('FixedConditionEnum')
+    rule = get_rule_by_name("FixedConditionEnum")
     assert len(rule.body.sequences) == 4
-    assert rule.body.sequences[0].repeatable_exprs[0]\
-                                 .expr.simple_match.match == 'all'
+    assert rule.body.sequences[0].repeatable_exprs[0].expr.simple_match.match == "all"
 
     # Referencing languages
     assert len(grammar_model.imports_or_references) == 3
     ref = grammar_model.imports_or_references[2]
-    assert ref.language_name == 'some-other-language'
-    assert ref.language_alias == 'o'
+    assert ref.language_name == "some-other-language"
+    assert ref.language_alias == "o"
diff --git a/tests/perf/test_callgraph_memoization.py b/tests/perf/test_callgraph_memoization.py
index bec98499b..7147b4c22 100644
--- a/tests/perf/test_callgraph_memoization.py
+++ b/tests/perf/test_callgraph_memoization.py
@@ -1,4 +1,5 @@
 from os.path import dirname, join
+
 from textx import metamodel_from_file
 
 
@@ -7,8 +8,8 @@ def callgraph_memoization():
 
     # Small file
     this_folder = dirname(__file__)
-    model = mm.model_from_file(join(this_folder,
-                                    'test_inputs', 'LightSwitch.rpy'))
+    mm.model_from_file(join(this_folder,
+                            'test_inputs', 'LightSwitch.rpy'))
 
 
 if __name__ == '__main__':
diff --git a/tests/perf/test_callgraph_nomemoization.py b/tests/perf/test_callgraph_nomemoization.py
index 2281f3b7d..aff2cfdd7 100644
--- a/tests/perf/test_callgraph_nomemoization.py
+++ b/tests/perf/test_callgraph_nomemoization.py
@@ -1,4 +1,5 @@
 from os.path import dirname, join
+
 from textx import metamodel_from_file
 
 
@@ -7,8 +8,8 @@ def callgraph_nomemoization():
 
     # Small file
     this_folder = dirname(__file__)
-    model = mm.model_from_file(join(this_folder,
-                                    'test_inputs', 'LightSwitch.rpy'))
+    mm.model_from_file(join(this_folder,
+                            'test_inputs', 'LightSwitch.rpy'))
 
 
 if __name__ == '__main__':
diff --git a/tests/perf/test_memory_memoization.py b/tests/perf/test_memory_memoization.py
index 884647c28..452601a49 100644
--- a/tests/perf/test_memory_memoization.py
+++ b/tests/perf/test_memory_memoization.py
@@ -1,5 +1,7 @@
 from os.path import dirname, join
+
 from memory_profiler import profile
+
 from textx import metamodel_from_file
 
 
@@ -9,12 +11,12 @@ def memoization():
 
     # Small file
     this_folder = dirname(__file__)
-    model = mm.model_from_file(join(this_folder,
-                                    'test_inputs', 'LightSwitch.rpy'))
+    mm.model_from_file(join(this_folder,
+                            'test_inputs', 'LightSwitch.rpy'))
 
     # Large file
-    model2 = mm.model_from_file(join(this_folder,
-                                     'test_inputs', 'LightSwitchDouble.rpy'))
+    mm.model_from_file(join(this_folder,
+                            'test_inputs', 'LightSwitchDouble.rpy'))
 
 
 if __name__ == '__main__':
diff --git a/tests/perf/test_memory_nomemoization.py b/tests/perf/test_memory_nomemoization.py
index bb04f1400..fe5bb186d 100644
--- a/tests/perf/test_memory_nomemoization.py
+++ b/tests/perf/test_memory_nomemoization.py
@@ -1,5 +1,7 @@
 from os.path import dirname, join
+
 from memory_profiler import profile
+
 from textx.metamodel import metamodel_from_file
 
 
@@ -9,12 +11,12 @@ def nomemoization():
 
     # Small file
     this_folder = dirname(__file__)
-    model = mm.model_from_file(join(this_folder,
-                                    'test_inputs', 'LightSwitch.rpy'))
+    mm.model_from_file(join(this_folder,
+                            'test_inputs', 'LightSwitch.rpy'))
 
     # Large file
-    model2 = mm.model_from_file(join(this_folder,
-                                     'test_inputs', 'LightSwitchDouble.rpy'))
+    mm.model_from_file(join(this_folder,
+                            'test_inputs', 'LightSwitchDouble.rpy'))
 
 
 if __name__ == '__main__':
diff --git a/tests/perf/test_speed.py b/tests/perf/test_speed.py
index f8a3f4cc9..ed9e821aa 100644
--- a/tests/perf/test_speed.py
+++ b/tests/perf/test_speed.py
@@ -1,4 +1,3 @@
-#-*- coding: utf-8 -*-
 #######################################################################
 # Testing parsing speed. This is used for the purpose of testing
 #   of performance gains/loses for various approaches.
@@ -6,17 +5,18 @@
 # Copyright: (c) 2016 Igor R. Dejanovic <igor DOT dejanovic AT gmail DOT com>
 # License: MIT License
 #######################################################################
-from __future__ import print_function, unicode_literals
 
 import time
-from os.path import dirname, join, getsize
+from os.path import dirname, getsize, join
+
 from textx.metamodel import metamodel_from_file
 
+
 def timeit(file_name, message, **kwargs):
     print(message, 'File:', file_name)
     file_name = join(dirname(__file__), 'test_inputs', file_name)
     file_size = getsize(file_name)
-    print('File size: {:.2f}'.format(file_size/1000), 'KB')
+    print(f'File size: {file_size/1000:.2f}', 'KB')
 
     mm = metamodel_from_file('rhapsody.tx', **kwargs)
 
@@ -24,8 +24,8 @@ def timeit(file_name, message, **kwargs):
     mm.model_from_file(file_name)
     t_end = time.time()
 
-    print('Elapsed time: {:.2f}'.format(t_end - t_start), 'sec')
-    print('Speed = {:.2f}'.format(file_size/1000/(t_end - t_start)), 'KB/sec\n')
+    print(f'Elapsed time: {t_end - t_start:.2f}', 'sec')
+    print(f'Speed = {file_size/1000/(t_end - t_start):.2f}', 'KB/sec\n')
 
 
 def main():
@@ -40,17 +40,17 @@ def main():
     print('\n*** No memoization\n')
     for i in range(3):
         timeit(file_name_small,
-               '{}. Small file, no memoization.'.format(i + 1))
+               f'{i + 1}. Small file, no memoization.')
         timeit(file_name_large,
-               '{}. Large file, no memoization.'.format(i + 1))
+               f'{i + 1}. Large file, no memoization.')
 
     print('\n*** Memoization\n')
     for i in range(3):
         timeit(file_name_small,
-               '{}. Small file, with memoization.'.format(i + 1),
+               f'{i + 1}. Small file, with memoization.',
                memoization=True)
         timeit(file_name_large,
-               '{}. Large file, with memoization.'.format(i + 1),
+               f'{i + 1}. Large file, with memoization.',
                memoization=True)
 
 if __name__ == '__main__':