How to configure Pycharm to use a Black install within a virtual environment


If you are using Pycharm with Pyenv (and possibly virtual environments more generally), you need to include the $PyInterpreterDirectory$ variable in the Black program path, so that Pycharm can find it within your selected virtual environment.

So the full program path to use would be $PyInterpreterDirectory$/black (if you are on Windows, use backslash).

Screenshot of Pycharm File Watcher config window showing configuration for Black formatter.
Screenshot of Pycharm File Watcher config window


I got a bit stuck following the standard Editor Integration instructions in the Black documentation:

The instructions advise you to use the which black command to find the path to your Black installation, and then use that path as the program path in Pycharm.

If you do that when using Pyenv (and potentially other virtual env setups), you get an error similar to this when trying to run Black:

The Black documentation is aimed at people who have a global installation of Black with a consistent program path across different projects.

Using the $PyInterpreterDirectory$ variable in the program path will ensure that Pycharm always tries to run the version of Black within the same virtual environment that you are using for running your project.

Verifying Slack requests in Python

If you’re building a Slack app in Python, you’ll need to verify that the inbound callbacks you’re receiving are genuine.

This page in the Slack API docs explains the process in pseudocode: Verifying requests from Slack

Below is a Python function, based on that pseudocode, which will simply return True if the request is valid, and False if not. This has been tested using a Flask request object, but should be easily adaptable to any other web framework.

I’ve declared the secrets in code here for ease of the example, but you shouldn’t do this in real life. You should put your secrets somewhere outside of the code (e.g. environment variables) and read them from config when needed.

import hmac
import hashlib
import time
def verify_request(request):
    SIGNING_SECRET = "your_slack_signing_secret"
    # Convert your signing secret to bytes
    slack_signing_secret = bytes(SIGNING_SECRET, "utf-8")
    request_body = request.get_data().decode()
    slack_request_timestamp = request.headers["X-Slack-Request-Timestamp"]
    slack_signature = request.headers["X-Slack-Signature"]
    # Check that the request is no more than 60 seconds old
    if (int(time.time()) - int(slack_request_timestamp)) > 60:
        print("Verification failed. Request is out of date.")
        return False
    # Create a basestring by concatenating the version, the request  
      timestamp, and the request body
    basestring = f"v0:{slack_request_timestamp}:{request_body}".encode("utf-8")
    # Hash the basestring using your signing secret, take the hex digest, and prefix with the version number
    my_signature = (
        "v0=" +, basestring, hashlib.sha256).hexdigest()
    # Compare the resulting signature with the signature on the request to verify the request
    if hmac.compare_digest(my_signature, slack_signature):
        return True
        print("Verification failed. Signature invalid.")
        return False

This could also be implemented as a Python decorator (if I get a chance I’ll update this post with an example of that too).

The “datetime.datetime not JSON serializable” thing

I hit this every time I start a new project involving APIs and data stored in PostgreSQL; and every time I spend ages googling to try and remember how to fix it.

The issue is that psycopg2 returns datetime.datetime types from database timestamp fields, which then can’t be converted when calling jsonify() on the returned dictionary.

There are actually quite a few ways to solve this and I’ve tended to use whichever one seems to hurt my head the least at the time…

Previously I’ve used the following method:

class DateEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj,
return obj.isoformat()
return JSONEncoder.default(self, obj)

But this time I discovered it can actually be as simple as:

json_friendly_date = meany_datetime_type.isoformat()

.isoformat() returns you a nice ISO8601 complaint string which will then jsonify() nicely

>> datetime.datetime(2015, 11, 9, 20, 30, 00).isoformat()