Python SDK
Python

The Blackfire Python SDK is available with the Blackfire Python Package.

The Blackfire Python SDK allows you to generate profiles from your code and eases the integration with third-party libraries.

The main entry point of the SDK is the blackfire module:

1
from blackfire import probe

The probe allows you to profile any parts of your code:

1
2
3
4
5
6
7
8
from blackfire import probe

probe.initialize()
probe.enable()

# some Python code you want to profile

probe.end()

The Blackfire Probe is usually configured via environment variables, but you can customize the configuration when calling the initialize() method via optional parameters:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from blackfire import probe

probe.initialize(
     # Client credentials. If None, they are read from environment variables, or config_file.
     client_id=None,
     client_token=None,
     # 1: error, 2: warning, 3: info, 4: debug
     log_level=None,
     # The destination of the log. i.e: /tmp/probe.log
     log_file=None,
     # Where to find the Blackfire CLI configuration file to retrieve the client_id and client_token values.
     config_file='~/.blackfire.ini',
     # The Blackfire Query, usually given by blackfire run or created on the fly by the SDK.
     query=None,
     # The network socket to use for contacting the agent
     agent_socket=None,
     # The connection timeout when connecting to the agent
     agent_timeout=None,
     # The Blackfire API endpoint
     endpoint=None,
 )

Please note that log_file and log_level parameters are deprecated and removed in version 1.10.0. You can configure the log file and level via the BLACKFIRE_LOG_FILE and BLACKFIRE_LOG_LEVELenvironment variables.

The SDK provides the run(call_end=True) method that simplifies the use of the enable() and disable() methods:

1
2
3
4
5
from blackfire import probe

with probe.run():
    foo()
    bar()

is equivalent to:

1
2
3
4
5
6
7
8
9
from blackfire import probe

probe.enable()
try:
    foo()
    bar()
finally:
    probe.disable()
    probe.end() # if call_end is True.

The SDK provides the @profile decorator, making it possible to profile functions separately:

1
2
3
4
5
6
7
8
from blackfire import profile

@profile
def foo():
    import time
    time.sleep(1.0)

foo()

In the snippet above, function foo() is using the @profile decorator. A probe instance is created by the decorator, using the configuration defined by environment variables.

You may specify a pair of Client ID/Token in the decorator:

1
2
3
4
5
6
7
8
from blackfire import profile

@profile(client_id = "my_client_id", client_token = "my_client_token")
def foo()
    import time
    time.sleep(1.0)

foo()
1
2
3
from blackfire import probe

probe.initialize()

Start profiling the code by calling enable():

1
2
# Start the profiling
probe.enable()

Stop profiling the code by calling disable():

1
2
# Stop the profiling
probe.disable()

You can call enable() and disable() as many times as needed in your code. You can also discard any collected data by calling clear_traces().

Calling end() instead of disable() stops the profiling and forces the collected data to be sent to Blackfire:

1
2
3
# Stop the profiling
# Send the result to blackfire
probe.end()

Thanks to the Distributed Profiling feature, you can embed sub-profiles in the main profile.

For instance, when profiling command line programs that call sub-processes, you might want to trigger profiles for them as well. You can do so by generating a sub-profile request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import subprocess
import sys
import os
from blackfire import probe

probe.initialize(
    client_id='xxxx',
    client_token='xxxx'
)

with probe.run():
    env = os.environ.copy()
    env['BLACKFIRE_QUERY'] = probe.generate_subprofile_query()
    _ = subprocess.run([sys.executable, "my_subprocess.py"], env=env)

Sub-profiles also work for HTTP requests by adding a X-Blackfire-Query HTTP header.

1
2
3
4
5
6
7
import requests
from blackfire import probe

with probe.run():
    url = 'https://mycustomapi.com'
    headers = {'X-Blackfire-Query:': probe.generate_subprofile_query()}
    r = requests.get(url, headers=headers)

The target process or HTTP server being sub-profiled may be written in any language supported by Blackfire.

With Markers, you can add cue-points to the Timeline View. This can be done by adding the following instruction in your code, where you want to add such a cue-point:

1
2
from blackfire import probe
probe.add_marker('My Marker Label')

Every SDK API call works under thread boundaries: enabling a profiler via `probe.enable()` enables it only for the current thread executing the API call. Same is true for other API calls as well: e.g., `probe.initialize()`, `probe.end()`...etc.

One interesting use case example of this feature might be to profile each worker thread individually in your favorite task queue, such as Celery.

As of version `1.14.0+`, multiple threads can be profiled simultaneously.

For example, you can call `probe.enable()` or `probe.end()` in different threads at the same time and in the same process. The profiler will handle the underlying segregation and aggregation of the profiling results.

This example profiles multiple threads simultaneously using the Sub-Profiles API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from blackfire import probe

THREAD_COUNT = 5

def _thread_worker(i, query):
    probe.initialize(query=query, title="thread-%d" % (i))
    probe.enable()
    # do stuff in worker thread
    probe.end()


probe.initialize(title="main-thread")
probe.enable()
# do stuff in main thread
probe.end()
ts = []
for i in range(THREAD_COUNT):
    query = probe.generate_subprofile_query()
    t = threading.Thread(target=_thread_worker, args=(
        i + 1,
        query,
    ))
    t.start()
    ts.append(t)
for t in ts:
    t.join()

This example generates a single profile, belonging to main-thread, and multiple linked Sub-Profiles.