byecycle 🚲¶
Find and expose cyclic imports in python projects.
Installation¶
byecycle uses the built-in ast module
to parse code files. As a consequence, it can only handle python code within the same
major version (read: no support for python 1 and 2), and the same or lower minor version
of the python interpreter it was installed with. If byecycle raises SyntaxErrors in
code that you know to be working, try using a byecycle that is installed with the same
python version that can run the code in question.
From PyPI¶
Requirements:¶
- python 3.11 or higher
- pipx
Development Setup¶
Requirements:¶
- python 3.11 or higher
- pdm
- git
Usage¶
As a Command Line Tool¶
# with a path
byecycle /home/me/dev/byecycle/src/byecycle/
# or the name of an installed package
byecycle byecycle
{
"byecycle.misc": {},
"byecycle.graph": {
"byecycle": {
"tags": [
"vanilla",
"parent"
],
"cycle": "complicated"
},
"byecycle.misc": {
"tags": [
"vanilla"
],
"cycle": null
}
},
[...]
"byecycle": {
"byecycle.graph": {
"tags": [
"vanilla",
"parent"
],
"cycle": "complicated"
}
}
}
--no-rich flag.
For bigger projects, you might get much more complex output. The intent of returning
json is to have something that can be easily piped into e.g. jq for further
processing:
# filter out imports that don't have a cycle
byecycle byecycle | jq '.[] |= (.[] |= select(.cycle != null) | select(. != {}))'
{
"byecycle.graph": {
"byecycle": {
"tags": [
"parent",
"vanilla"
],
"cycle": "complicated"
}
},
"byecycle.cli": {
"byecycle": {
"tags": [
"parent",
"vanilla"
],
"cycle": "complicated"
}
},
"byecycle": {
"byecycle.graph": {
"tags": [
"parent",
"vanilla"
],
"cycle": "complicated"
},
"byecycle.cli": {
"tags": [
"parent",
"vanilla"
],
"cycle": "complicated"
}
}
}
from byecycle import run
cycles, *_ = run("byecycle")
# filter out imports that don't have a cycle
for outer_k, outer_v in cycles.items():
for inner_k, inner_v in outer_v.items():
if inner_v["cycle"]:
print(f"{outer_k} -> {inner_k}: {inner_v['cycle']}")
byecycle.graph -> byecycle -> complicated
byecycle.cli -> byecycle -> complicated
byecycle -> byecycle.graph -> complicated
byecycle -> byecycle.cli -> complicated
See the help text of byecycle for an explanation of tags/ImportKinds and
cycle/EdgeKinds.
In short, if there is a cycle, the tags of all involved imports inform the cycle-severity, with the highest severity winning out if multiple apply. The defaults can be overriden in order to isolate, filter, or highlight cycles with specific severities.
To Visualize the Import Graph¶
If you pass the --draw flag1 on your command-line-call, byecycle will create an image of
the import graph instead:
[1] Requires installation of the draw-extra, i.e. pipx install "byecycle[draw]".