Skip to content

Conversation

@Spectre5
Copy link
Owner

Overview

This is a fairly major PR to integrate several tools to aide in code quality and consistency. After this is agreed to and merged, with modifications as necessary, I'll follow up with another PR to add CI (continuous integration) to perform some checks when new PRs are opened. Below I lay out my reasoning for incorporating each tool and explain which options I've configured for each.

A quick note on configuration files --- all of the tools below have their own custom configuration files, but many tools in the python community have also moved towards allowing configuration via the pyproject.toml file and/or the setup.cfg file. Some people prefer each tool to have its own custom/separate configuration file, but I personally hate having a ton of extra configuration files and thus I prefer using the two aforementioned files for configuration in any tool that supports them, with a preference towards the pyproject.toml file since it is more often supported.

Also, it is worth noting that each of these tools have global options set via the various configuration files, but they all support locally disabling them (in an entire file or in certain lines of the file) via comments in the source files as well. In the interest of not making this PR exceedingly long, I'll let you read their documentation to find out how.

I'll now briefly describe each tool and my thoughts, but be sure to read the Setup section for a quick rundown on how you'd use this in practice. It is also worth mentioning that these tools are supported in most editors, such as VSCodium, vscode, PyCharm, etc. So you can format on save or by shortcut, get linting issues reported in real time, etc.

Setup

I've included some new setup instructions in the CONTRIBUTING.rst file regarding how to get up-and-going with poetry (via pipx) for anyone unfamiliar with it, so please do see it. I will include here the basics to get started, assuming that you don't have pipx nor poetry already installed.

python3 -m pip install pipx
pipx install poetry
pipx ugprade poetry
pipx ensurepath

Custom runner script

There is a file in the top directory simply named runner, which is a bash shell script. It can be used on windows as well via git-bash if you've installed Git for Windows. Make should also be installed separately when used on Windows so that the docs build. This script simply provides a single interface to run the tools below to fully check the code passes each tool as well as the tests. The idea (hope) is that if you pass all checks from this script, then you should pass the CI checks when you make a PR (CI checks to come later in a separate PR). I won't go through all of the details here as you can just run ./runner --help to see the options, or read the file itself which is rather simple and straight forward.

Poetry

Someone mentioned in a recent issue or PR the importance of being able to exactly re-produce an older copy of section-properties, especially since it could be used in real engineering applications. Poetry is a great tool to explicitly lock the entire dependency tree, including all sub-dependencies, via the poetry.lock file. Thus with this file, anyone could grab an old release and exactly reproduce the entire environment (if and only if installed using the lock file --- installing with pip from PyPI does not know about and does not use the lock file!). During developement, we can use this lock file to ensure that we all have identical virutal environments setup, which may aide in finding issues that only some of us experience. It is important to note though that when installed via PyPI, the lock file is not used and the exact versions of libraries the user has will thus be different/uncertain.

Poetry requires use of the pyproject.toml file. The recommended way to use Poetry is to install it outside of your virtual environment (I like to use pipx for this) and then use Poetry for the entire project including creation and setup of the virtual environment.

black

black is the infamous "opinionated" python code formatter. I can assure you that you'll fight it at first because it will format some things differently than your preference. Being opinionated, it intentially provides very few configuration options, so you basically have to just accept what it does, or not use it. The intention is to just use black and not spend time arguing or deciding what the best formatting standard to follow is. After you get used to it, you'll likely love it and use it all the time and even grow to appreciate some of the "weird" formatting you didn't like at the start.

black is only configurable via the pyproject.toml file. As I mentioned, there are very few options that you can change, but two of them that I have adjusted here are:

  • set the line length to 100, per this projects preference
  • skip string normalization - by default black generally changes all quotes from single quotes to double. With this option, it just leaves all quotes as-is, leaving them as double or as single quotes. I personally strongly prefer single quotes as it is much less noisy and easier to read the code with them (for me). I couldn't accept using black years ago for my personal projects until they finally relented to adding this option, but they still refused to add an option to convert strings to single quotes!

isort

isort is used to sort your imports. Best practice is generally to put standard library imports, then third party libraries, and last your own first party imports. Unlike black, isort has quite a few configuration options. Some of them need to be set to specific values to make isort compatible with black, but others can still be adjusted. For example, it can sort each "section" by alphabetical order (default) or by line length (option length_sort).

Starting with version 5.0.0, isort introduced "profiles" to automatically set certain options. One of them is a black profile so that isort and black work nicely together.

I've setup isort configuration in the pyproject.toml file and it has just a few options set:

  • use black profile for compatibility with black
  • set the line length to 100
  • src_paths to tell isort which directories are always considered first party (your library)

flake8 + friends

flake8 wraps a couple of tools and is a very quick and useful tool to find common linting issues and recommended code improvements. It also supports plugins or extensions to add additional checks. I've included here the pluging flake8-docstring which adds an extension to use pydocstyle to enforce improved docstring consistency. I've also added flake8-bugbear which includes some additional checks and bug finding features. Finally, I've added flake8-rst-docstrings which can be used to check that the docstrings contain valid reStructuredText.

I've setup flake8 configuration in the setup.cfg file. flake8 is fairly configurable, but I've only made a few changes to the defaults:

  • set line length to 100
  • ignore E203 (must ignore this when using black) plus some additional ignores that we should eventually remove (but I don't want to make too many code changes to fix all flags in 1 PR)
  • set max complexity to 20
  • set the exclude list
  • add a few more exclusions (ignores) for just the tests

pylint

pylint is a linter + static code analysis tool that is quite a bit more complex than flake8, usually more noisy, and significantly slower. It supports a huge amount of customization though to allow only using the parts of it that you want. The static analysis part catches some poor idioms and errors that flake8 cannot detect, so I do like to use it in addition to flake8.

I've setup pylint configuration in the pyproject.toml file. pylint offers a huge amount of configuration and you could probably spend a solid weekend looking through it all. I've only changed a few here:

  • set line length to 100
  • set jobs to 0 (allow parallel execution of pylint)
  • added numpy to the extension whitelist, useful for libraries with dynamic imports (though they still sometimes cause issues)
  • added some "good names" that pylint won't complain about
  • disabled some of the messages. Messages added to this disable list are disabled globally, in all files. This is probably the most important option to configure based on what checks you decide you care about. I've added to this list a handful of checks already, but this is a point that probably needs some discussion to decide as a project which ones we do or don't care about. Some of these disabled messages should definitely be removed in the future, but I don't want to make too many code changes in this PR and make it overwhelming.

I still get false positives with pylint or have certain functions or branches of code that I want to locally disable specific checks for. Although all of the above tools support local disables in code, I rarely use them except for pylint. These are usually via a comment # pylint: disable=option-name-here. I find with pylint that the big decision for it comes down to which checks you decide to put in the global disable list and which ones you want to only disable occasionally within the code via comments.

rstcheck

rstcheck is a tool to perform checks on rst files to ensure they are valid. Note that it has not been updated in a few years, so this might be a dead project. Nevertheless, it works well enough for now.

Configuration of this tool is in the setup.cfg file.

pre-commit

pre-commit is a package to install git hooks to perform checks on code before it is committed. Regardless of whether this is used, whatever requirements are agreed upon should be enforced via CI, but I like using pre-commit git hooks so that I can fix issues locally before even bothering to open the PR and having the CI fail. Even after installation, you can ignore the hooks if needed via the no verify option to git, git commit --no-verify ...

Configuration for this is in the .pre-commit-config.yaml file. The normal, recommended way to use these hooks are to point to the git repository for each tool you use and let the python pre-commit tool make virtual enviornments and manage those tools. I don't like this idea as then you have to maintain the version for each dev dependency in two locations (this config file and the pyproject.toml file). Plus, if I'm using the tool as a dev dependency anyway, then it is pointless to make another environment when my local project environment already has the correct tool and correct version installed. Thus, I've set all of these hooks to use system and poetry to run the tool from the local virtual environment instead. So if you look into the documentation on this tool and see configurations a bit different than what I have, that is why.

pytest

I believe we've all agreed to incorporate pytest for testing, which is very common in the python community.

I've puts its configuration into the pyproject.toml file consistent with the other tools above.

Other Tools (Not Used)

There are many other tools available that could potentially be used in addition, or in place of, the tools I've incorporated and mentioned above. My selection of the above tools is certainly based on my own bias, as they are what I regularly use in other projects. However, I also feel that they are the most common ones used in other python projects I've encountered or been involved with. Nevertheless, for posterity, I'll briefly mention a few others. Note that my experience with most of these is minimal:

  • pydocstyle - This actually is used via the flake8-docstring plugin and provides docstring checking
  • mypy - performs static checking and I think that we should use and incorporate this when (or if) we update section-properties to use type annotations as without them this tool is not useful - I've actually included an initial configuration for it in the setup.cfg file, but it isn't currently used
  • dodgy - tries to help prevent you accidentally committing code with personal information like passwords, secrets, etc
  • prospector - brings together a few tools under one project, but all are already covered by using pylint and flake8
  • pylama - annother conglomerate tool to bring together various tools into 1 interface, but is also mostly covered by those already used
  • pychecker - finds bugs, I think via a similar method to pylint (I've never used this one)
  • bandit - an interesting tool that looks for security vulnerabilities within your code
  • Radon - tool to compute some various metrics on your code, such as an estimate of how maintainable it is, how complex it is, etc - this one seems pretty interesting, but I've never used it
  • eradicate - removes commented out code, as it is generally considered poor practice; with git you don't need that old commented out code!
  • various flake8 plugins - flake8 can easily be extended and there are many, many plugins (extensions) available for flake8, which can bring multiple tools under it. We've already used 2, but there are countless others including plugins for some other tools mentioned previously (i.e. eradicate, bandit, and isort). Whether you use a tool separate or under flake8 is really a matter of personal preference. I prefer to keep isort separate (it is a formatter, not a linter), so I've put it separate. I could also have kept pydocstyle separate, but I do like its integration with flake8.
  • etc... - there are many others, this is certainly not an exhaustive list

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant