Is there a way how to use flake8 from python 3.11, but restrict it to follow rules of python 3.9?
e.g. for mypy, I can add --python-version=X.Y
This flag will make mypy type check your code as if it were run under Python version X.Y. Without this option, mypy will default to using whatever version of Python is running mypy.
flake8 itself is a linting framework and not a tool actually a linter. this distinction is somewhat important to this question because the actual implementation of lint rules is up to whatever plugins you add to flake8. I tend to refer to flake8 though as just "pyflakes + pycodestyle + some glue" so you can generalize that to what checks it's performing.
flake8 parses the ast and tokenization using the current python version you are executing and passes those along to tools (generally they use those representations to produce diagnostics -- though some ignore them and re-parse / alternatively parse themselves). flake8 does this very intentionally so the syntax features are exactly the python it is running and nothing else.
to run flake8 against python 3.9 code with the most accuracy you need to install flake8 to an interpreter which matches
this is called out in the very first section of the docs
It is very important to install Flake8 on the correct version of Python for your needs. If you want Flake8 to properly parse new language features in Python 3.5 (for example), you need it to be installed on 3.5 for Flake8 to understand those features. In many ways, Flake8 is tied to the version of Python on which it runs.
disclaimer: I currently maintain flake8