pycfg-rs

Fast intra-procedural control flow graph generation for Python.

No runtime required. Text, JSON, and DOT output for humans, machines, and agents.

Get started

$ git clone https://github.com/nwyin/pycfg-rs && cd pycfg-rs
$ cargo build --release
$ target/release/pycfg src/app/service.py::UserService.get_profile

Python source

def process(request):
    try:
        user = authenticate(request)
        if user.is_admin:
            return admin_dashboard(user)
        return user_dashboard(user)
    except AuthError:
        return redirect("/login")
    finally:
        log_access(request)

What pycfg-rs produces

$ pycfg src/app/service.py::process

Function: process (line 1)
  Block 0 (entry):
    [L2] try:
    -> 1 (try-body)
    -> 3 (exception: AuthError)
  Block 1 (try-body):
    [L3] user = authenticate(request)
    [L4] if user.is_admin:
    -> 2 (True)
    -> 5 (False)
  Block 2 (if-true):
    [L5] return admin_dashboard(user)
    -> 4 (finally)
  Block 3 (except: AuthError):
    [L7] return redirect("/login")
    -> 4 (finally)
  Block 4 (finally):
    [L9] log_access(request)
    -> 6 (exit)
  Block 5 (if-false):
    [L6] return user_dashboard(user)
    -> 4 (finally)
  Block 6 (exit):

  Metrics: 7 blocks, 8 edges, 2 branches, CC=4

How it compares

Speed measured on the rich corpus (100 files, 984 functions). * python-graphs only analyzed 13% of files due to import requirements. Roadmap and limitations.

pycfg-rs staticfg python-graphs
Speed (rich, 100 files) 65 ms 84 ms 7 ms*
Per-function speed 0.066 ms 0.35 ms 0.19 ms
File coverage 100% 74% 13%
Output formats text / JSON / DOT DOT only Python object
Python 3.10+ (match/case) yes no no
Runtime required no no yes (import)
Language Rust Python Python
Maintained active unmaintained archived

Corpus results

Analysis of 9 popular open-source Python projects, run on every push. Expand a project to see complexity distribution and CFG visualizations.

Throughput: 29,197 functions/sec across 9 projects Total: 7,197 functions in 429 files Time: 246 ms Coverage: 9/9 projects passing
pytest 69 files 2,016 functions avg CC 2.6 57ms pass
69 files
2,016 functions
2.6 avg CC
28 max CC
35,416 fn/s
Complexity distribution: low (<5): 1703 moderate (5-9): 259 high (10-19): 51 very high (20+): 3

Top 5 most complex functions

RaisesGroup._check_exceptions CC 28
pytest/src/_pytest/raises.py:1266 53 blocks, 79 edges
Show CFG
Rendering graph...
LocalPath.make_numbered_dir CC 24
pytest/src/_pytest/_py/path.py:1274 54 blocks, 76 edges
Show CFG
Rendering graph...
Session.collect CC 24
pytest/src/_pytest/main.py:886 49 blocks, 71 edges
Show CFG
Rendering graph...
LocalPath.pyimport CC 19
pytest/src/_pytest/_py/path.py:1070 41 blocks, 58 edges
Show CFG
Rendering graph...
assertrepr_compare CC 19
pytest/src/_pytest/assertion/util.py:178 39 blocks, 56 edges
Show CFG
Rendering graph...
pydantic 105 files 1,792 functions avg CC 2.9 62ms pass
105 files
1,792 functions
2.9 avg CC
50 max CC
28,854 fn/s
Complexity distribution: low (<5): 1503 moderate (5-9): 202 high (10-19): 74 very high (20+): 13

Top 5 most complex functions

traverse_schema CC 50
pydantic/pydantic/_internal/_schema_gather.py:96 101 blocks, 149 edges
Show CFG
Rendering graph...
_apply_constraint CC 50
pydantic/pydantic/experimental/pipeline.py:443 101 blocks, 149 edges
Show CFG
Rendering graph...
GenerateSchema.match_type CC 49
pydantic/pydantic/_internal/_generate_schema.py:1035 98 blocks, 145 edges
Show CFG
Rendering graph...
ModelField._type_analysis CC 45
pydantic/pydantic/v1/fields.py:581 95 blocks, 138 edges
Show CFG
Rendering graph...
ModelMetaclass.__new__ CC 31
pydantic/pydantic/v1/main.py:123 67 blocks, 96 edges
Show CFG
Rendering graph...
rich 100 files 984 functions avg CC 2.5 42ms pass
100 files
984 functions
2.5 avg CC
36 max CC
23,316 fn/s
Complexity distribution: low (<5): 864 moderate (5-9): 86 high (10-19): 29 very high (20+): 5

Top 5 most complex functions

traverse._traverse CC 36
rich/rich/pretty.py:621 87 blocks, 121 edges
Show CFG
Rendering graph...
Markdown.__rich_console__ CC 23
rich/rich/markdown.py:578 48 blocks, 69 edges
Show CFG
Rendering graph...
Style.__str__ CC 21
rich/rich/style.py:290 42 blocks, 61 edges
Show CFG
Rendering graph...
legacy_windows_render CC 20
rich/rich/_windows_renderer.py:7 42 blocks, 60 edges
Show CFG
Rendering graph...
Table._render CC 20
rich/rich/table.py:755 44 blocks, 62 edges
Show CFG
Rendering graph...
click 17 files 530 functions avg CC 2.7 16ms pass
17 files
530 functions
2.7 avg CC
33 max CC
32,444 fn/s
Complexity distribution: low (<5): 449 moderate (5-9): 64 high (10-19): 15 very high (20+): 2

Top 5 most complex functions

Option.__init__ CC 33
click/src/click/core.py:2709 69 blocks, 100 edges
Show CFG
Rendering graph...
Option.get_help_extra CC 21
click/src/click/core.py:3033 49 blocks, 68 edges
Show CFG
Rendering graph...
Command.main CC 17
click/src/click/core.py:1338 30 blocks, 45 edges
Show CFG
Rendering graph...
open_url CC 16
click/src/click/_termui_impl.py:676 43 blocks, 57 edges
Show CFG
Rendering graph...
Context.__init__ CC 16
click/src/click/core.py:273 34 blocks, 48 edges
Show CFG
Rendering graph...
httpx 23 files 468 functions avg CC 2.2 13ms pass
23 files
468 functions
2.2 avg CC
17 max CC
37,095 fn/s
Complexity distribution: low (<5): 416 moderate (5-9): 50 high (10-19): 2 very high (20+): 0

Top 5 most complex functions

urlparse CC 17
httpx/httpx/_urlparse.py:213 32 blocks, 47 edges
Show CFG
Rendering graph...
get_environment_proxies CC 10
httpx/httpx/_utils.py:30 21 blocks, 29 edges
Show CFG
Rendering graph...
create_ssl_context CC 9
httpx/httpx/_config.py:23 21 blocks, 28 edges
Show CFG
Rendering graph...
trace CC 9
httpx/httpx/_main.py:212 18 blocks, 25 edges
Show CFG
Rendering graph...
URL.__init__ CC 9
httpx/httpx/_urls.py:77 19 blocks, 26 edges
Show CFG
Rendering graph...
black 25 files 436 functions avg CC 4.4 21ms pass
25 files
436 functions
4.4 avg CC
84 max CC
20,864 fn/s
Complexity distribution: low (<5): 302 moderate (5-9): 91 high (10-19): 36 very high (20+): 7

Top 5 most complex functions

whitespace CC 84
black/src/black/nodes.py:180 162 blocks, 244 edges
Show CFG
Rendering graph...
get_features_used CC 34
black/src/black/__init__.py:1361 69 blocks, 101 edges
Show CFG
Rendering graph...
main CC 32
black/src/black/__init__.py:255 66 blocks, 96 edges
Show CFG
Rendering graph...
normalize_invisible_parens CC 25
black/src/black/linegen.py:1476 51 blocks, 74 edges
Show CFG
Rendering graph...
can_omit_invisible_parens CC 23
black/src/black/lines.py:939 45 blocks, 66 edges
Show CFG
Rendering graph...
flask 24 files 407 functions avg CC 2.2 12ms pass
24 files
407 functions
2.2 avg CC
17 max CC
35,178 fn/s
Complexity distribution: low (<5): 366 moderate (5-9): 34 high (10-19): 7 very high (20+): 0

Top 5 most complex functions

Blueprint.register CC 17
flask/src/flask/sansio/blueprints.py:273 35 blocks, 50 edges
Show CFG
Rendering graph...
Flask.make_response CC 14
flask/src/flask/app.py:1224 33 blocks, 45 edges
Show CFG
Rendering graph...
Flask.run CC 11
flask/src/flask/app.py:632 28 blocks, 37 edges
Show CFG
Rendering graph...
Flask.url_for CC 11
flask/src/flask/app.py:1102 26 blocks, 35 edges
Show CFG
Rendering graph...
find_app_by_string CC 11
flask/src/flask/cli.py:120 28 blocks, 37 edges
Show CFG
Rendering graph...
fastapi 48 files 313 functions avg CC 2.8 15ms pass
48 files
313 functions
2.8 avg CC
33 max CC
21,041 fn/s
Complexity distribution: low (<5): 270 moderate (5-9): 30 high (10-19): 6 very high (20+): 7

Top 5 most complex functions

get_openapi_path CC 33
fastapi/fastapi/openapi/utils.py:260 64 blocks, 95 edges
Show CFG
Rendering graph...
analyze_param CC 32
fastapi/fastapi/dependencies/utils.py:393 52 blocks, 82 edges
Show CFG
Rendering graph...
get_request_handler.app CC 28
fastapi/fastapi/routing.py:382 57 blocks, 83 edges
Show CFG
Rendering graph...
jsonable_encoder CC 27
fastapi/fastapi/encoders.py:112 55 blocks, 80 edges
Show CFG
Rendering graph...
get_openapi CC 24
fastapi/fastapi/openapi/utils.py:514 48 blocks, 70 edges
Show CFG
Rendering graph...
requests 18 files 251 functions avg CC 2.8 9ms pass
18 files
251 functions
2.8 avg CC
18 max CC
27,971 fn/s
Complexity distribution: low (<5): 212 moderate (5-9): 27 high (10-19): 12 very high (20+): 0

Top 5 most complex functions

HTTPAdapter.send CC 18
requests/src/requests/adapters.py:591 37 blocks, 53 edges
Show CFG
Rendering graph...
HTTPDigestAuth.build_digest_header CC 16
requests/src/requests/auth.py:126 35 blocks, 49 edges
Show CFG
Rendering graph...
RequestEncodingMixin._encode_files CC 15
requests/src/requests/models.py:138 33 blocks, 46 edges
Show CFG
Rendering graph...
PreparedRequest.prepare_url CC 15
requests/src/requests/models.py:411 34 blocks, 47 edges
Show CFG
Rendering graph...
PreparedRequest.prepare_body CC 14
requests/src/requests/models.py:496 35 blocks, 47 edges
Show CFG
Rendering graph...