import-effects 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- import_effects-0.0.1/.github/ISSUE_TEMPLATE/bug.yml +33 -0
- import_effects-0.0.1/.github/ISSUE_TEMPLATE/feature.yml +19 -0
- import_effects-0.0.1/.github/PULL_REQUEST_TEMPLATE.md +11 -0
- import_effects-0.0.1/.github/dependabot.yml +11 -0
- import_effects-0.0.1/.github/workflows/ci.yml +79 -0
- import_effects-0.0.1/.github/workflows/release.yml +48 -0
- import_effects-0.0.1/.gitignore +24 -0
- import_effects-0.0.1/CHANGELOG.md +16 -0
- import_effects-0.0.1/CODE_OF_CONDUCT.md +8 -0
- import_effects-0.0.1/CONTRIBUTING.md +20 -0
- import_effects-0.0.1/LICENSE +22 -0
- import_effects-0.0.1/PKG-INFO +169 -0
- import_effects-0.0.1/README.md +133 -0
- import_effects-0.0.1/SECURITY.md +14 -0
- import_effects-0.0.1/docs/api.md +24 -0
- import_effects-0.0.1/docs/architecture.md +28 -0
- import_effects-0.0.1/docs/assets/demo.gif +0 -0
- import_effects-0.0.1/examples/demo_bad_package.py +31 -0
- import_effects-0.0.1/pyproject.toml +89 -0
- import_effects-0.0.1/scripts/render_demo.py +155 -0
- import_effects-0.0.1/src/import_effects/__init__.py +15 -0
- import_effects-0.0.1/src/import_effects/__main__.py +3 -0
- import_effects-0.0.1/src/import_effects/_probe.py +321 -0
- import_effects-0.0.1/src/import_effects/cli.py +175 -0
- import_effects-0.0.1/src/import_effects/inspector.py +124 -0
- import_effects-0.0.1/src/import_effects/models.py +85 -0
- import_effects-0.0.1/src/import_effects/py.typed +1 -0
- import_effects-0.0.1/tests/conftest.py +15 -0
- import_effects-0.0.1/tests/fixtures/broken_package.py +1 -0
- import_effects-0.0.1/tests/fixtures/clean_package.py +1 -0
- import_effects-0.0.1/tests/fixtures/effects_package.py +43 -0
- import_effects-0.0.1/tests/fixtures/output_package.py +1 -0
- import_effects-0.0.1/tests/fixtures/slow_package.py +3 -0
- import_effects-0.0.1/tests/test_cli.py +111 -0
- import_effects-0.0.1/tests/test_inspector.py +90 -0
- import_effects-0.0.1/tests/test_models.py +14 -0
- import_effects-0.0.1/tests/test_probe.py +91 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Report incorrect or missing import observations
|
|
3
|
+
title: "bug: "
|
|
4
|
+
labels: [bug]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: Do not paste secrets or reports containing private paths without sanitizing them.
|
|
9
|
+
- type: input
|
|
10
|
+
id: version
|
|
11
|
+
attributes:
|
|
12
|
+
label: import-effects version
|
|
13
|
+
validations:
|
|
14
|
+
required: true
|
|
15
|
+
- type: input
|
|
16
|
+
id: platform
|
|
17
|
+
attributes:
|
|
18
|
+
label: Python and operating system
|
|
19
|
+
validations:
|
|
20
|
+
required: true
|
|
21
|
+
- type: textarea
|
|
22
|
+
id: reproduction
|
|
23
|
+
attributes:
|
|
24
|
+
label: Minimal reproduction
|
|
25
|
+
validations:
|
|
26
|
+
required: true
|
|
27
|
+
- type: textarea
|
|
28
|
+
id: expected
|
|
29
|
+
attributes:
|
|
30
|
+
label: Expected behavior
|
|
31
|
+
validations:
|
|
32
|
+
required: true
|
|
33
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Propose a reliable new observation or developer workflow
|
|
3
|
+
title: "feat: "
|
|
4
|
+
labels: [enhancement]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: problem
|
|
8
|
+
attributes:
|
|
9
|
+
label: Problem
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: evidence
|
|
14
|
+
attributes:
|
|
15
|
+
label: Reliable runtime evidence or API
|
|
16
|
+
description: Explain how the behavior can be observed without overstating attribution.
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ci-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
quality:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v5
|
|
21
|
+
with:
|
|
22
|
+
fetch-depth: 0
|
|
23
|
+
- uses: actions/setup-python@v6
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.12"
|
|
26
|
+
cache: pip
|
|
27
|
+
- run: python -m pip install -e '.[dev]'
|
|
28
|
+
- run: ruff format --check .
|
|
29
|
+
- run: ruff check .
|
|
30
|
+
- run: mypy
|
|
31
|
+
- run: pytest --cov --cov-report=term-missing --cov-report=xml
|
|
32
|
+
- run: python -m build
|
|
33
|
+
- run: twine check dist/*
|
|
34
|
+
- name: Inspect distributions
|
|
35
|
+
run: |
|
|
36
|
+
python -m zipfile -l dist/*.whl
|
|
37
|
+
tar -tzf dist/*.tar.gz
|
|
38
|
+
- name: Install Gitleaks
|
|
39
|
+
run: |
|
|
40
|
+
curl --fail --location --silent --show-error \
|
|
41
|
+
https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz \
|
|
42
|
+
| tar -xz -C /tmp gitleaks
|
|
43
|
+
- name: Scan repository history
|
|
44
|
+
run: /tmp/gitleaks git --redact --exit-code 1 .
|
|
45
|
+
|
|
46
|
+
test:
|
|
47
|
+
strategy:
|
|
48
|
+
fail-fast: false
|
|
49
|
+
matrix:
|
|
50
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
51
|
+
python: ["3.10", "3.12", "3.14"]
|
|
52
|
+
exclude:
|
|
53
|
+
- os: macos-latest
|
|
54
|
+
python: "3.10"
|
|
55
|
+
- os: windows-latest
|
|
56
|
+
python: "3.10"
|
|
57
|
+
runs-on: ${{ matrix.os }}
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@v5
|
|
60
|
+
- uses: actions/setup-python@v6
|
|
61
|
+
with:
|
|
62
|
+
python-version: ${{ matrix.python }}
|
|
63
|
+
cache: pip
|
|
64
|
+
- run: python -m pip install -e '.[dev]'
|
|
65
|
+
- run: pytest
|
|
66
|
+
|
|
67
|
+
wheel-smoke:
|
|
68
|
+
runs-on: ubuntu-latest
|
|
69
|
+
steps:
|
|
70
|
+
- uses: actions/checkout@v5
|
|
71
|
+
- uses: actions/setup-python@v6
|
|
72
|
+
with:
|
|
73
|
+
python-version: "3.12"
|
|
74
|
+
- run: python -m pip install build
|
|
75
|
+
- run: python -m build
|
|
76
|
+
- run: python -m venv /tmp/import-effects-consumer
|
|
77
|
+
- run: /tmp/import-effects-consumer/bin/pip install dist/*.whl
|
|
78
|
+
- run: /tmp/import-effects-consumer/bin/import-effects json --quiet
|
|
79
|
+
- run: /tmp/import-effects-consumer/bin/python -c "from import_effects import inspect_import; assert inspect_import('json').success"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: Publish PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
id-token: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
if: startsWith(github.event.release.tag_name, 'v')
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
environment: pypi
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v5
|
|
18
|
+
- uses: actions/setup-python@v6
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
- run: python -m pip install build twine
|
|
22
|
+
- run: python -m build
|
|
23
|
+
- run: twine check dist/*
|
|
24
|
+
- name: Check whether this version is already published
|
|
25
|
+
id: registry
|
|
26
|
+
run: |
|
|
27
|
+
version="${GITHUB_REF_NAME#v}"
|
|
28
|
+
if python - "$version" <<'PY'
|
|
29
|
+
import sys
|
|
30
|
+
import urllib.error
|
|
31
|
+
import urllib.request
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
urllib.request.urlopen(
|
|
35
|
+
f"https://pypi.org/pypi/import-effects/{sys.argv[1]}/json", timeout=15
|
|
36
|
+
)
|
|
37
|
+
except urllib.error.HTTPError as error:
|
|
38
|
+
raise SystemExit(1 if error.code == 404 else 2)
|
|
39
|
+
PY
|
|
40
|
+
then
|
|
41
|
+
echo "published=true" >> "$GITHUB_OUTPUT"
|
|
42
|
+
else
|
|
43
|
+
echo "published=false" >> "$GITHUB_OUTPUT"
|
|
44
|
+
fi
|
|
45
|
+
- name: Publish with trusted publishing
|
|
46
|
+
if: steps.registry.outputs.published != 'true'
|
|
47
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
48
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
.venv/
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.coverage
|
|
6
|
+
coverage.xml
|
|
7
|
+
htmlcov/
|
|
8
|
+
.mypy_cache/
|
|
9
|
+
.pytest_cache/
|
|
10
|
+
.ruff_cache/
|
|
11
|
+
build/
|
|
12
|
+
dist/
|
|
13
|
+
*.whl
|
|
14
|
+
*.tar.gz
|
|
15
|
+
.env
|
|
16
|
+
.env.*
|
|
17
|
+
!.env.example
|
|
18
|
+
.DS_Store
|
|
19
|
+
.idea/
|
|
20
|
+
.vscode/
|
|
21
|
+
docs/assets/*.mp4
|
|
22
|
+
launch*.md
|
|
23
|
+
x-launch*.md
|
|
24
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
|
|
5
|
+
## [0.0.1] - 2026-09-22
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Fresh-subprocess import inspection with timeout handling.
|
|
10
|
+
- Audit-based file, network, subprocess, and filesystem observations.
|
|
11
|
+
- Thread, multiprocessing, environment, cwd, logging, path, warning, and signal observations.
|
|
12
|
+
- Typed Python API, JSON output, policy exits, ignores, and assertion helper.
|
|
13
|
+
- CPython 3.10 through 3.14 CI across Linux, macOS, and Windows.
|
|
14
|
+
|
|
15
|
+
[0.0.1]: https://github.com/royalpinto007/import-effects/releases/tag/v0.0.1
|
|
16
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
We pledge to make participation in this project a harassment-free experience for everyone. Be respectful, constructive, and considerate.
|
|
4
|
+
|
|
5
|
+
Unacceptable behavior includes harassment, discriminatory language, personal attacks, publishing private information, or other conduct inappropriate in a professional community.
|
|
6
|
+
|
|
7
|
+
Report conduct concerns privately to royalpinto007@gmail.com. Maintainers may remove, edit, or reject contributions and ban participants whose behavior violates this code.
|
|
8
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Contributions are welcome. Please open an issue before changing public behavior or adding a new effect kind.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
python -m venv .venv
|
|
7
|
+
. .venv/bin/activate
|
|
8
|
+
pip install -e '.[dev]'
|
|
9
|
+
ruff format .
|
|
10
|
+
ruff check .
|
|
11
|
+
mypy
|
|
12
|
+
pytest --cov
|
|
13
|
+
python -m build
|
|
14
|
+
twine check dist/*
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Tests should exercise behavior through the public API or CLI in a real child interpreter. Do not add observations that cannot be reproduced reliably, and keep confidence and attribution claims conservative.
|
|
18
|
+
|
|
19
|
+
Use Conventional Commits and do not commit captured environment data, tokens, local package sources, or generated artifacts.
|
|
20
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Royal Pinto
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: import-effects
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: See what Python does when you import.
|
|
5
|
+
Project-URL: Homepage, https://github.com/royalpinto007/import-effects
|
|
6
|
+
Project-URL: Documentation, https://github.com/royalpinto007/import-effects#readme
|
|
7
|
+
Project-URL: Issues, https://github.com/royalpinto007/import-effects/issues
|
|
8
|
+
Project-URL: Source, https://github.com/royalpinto007/import-effects
|
|
9
|
+
Author-email: Royal Pinto <royalpinto007@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: audit-hooks,cli,debugging,imports,observability,python,security,side-effects,testing
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
25
|
+
Classifier: Topic :: Software Development :: Debuggers
|
|
26
|
+
Classifier: Topic :: Software Development :: Testing
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: build>=1.3; extra == 'dev'
|
|
30
|
+
Requires-Dist: mypy>=1.17; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-cov>=6.2; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8.4; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff>=0.12; extra == 'dev'
|
|
34
|
+
Requires-Dist: twine>=6.2; extra == 'dev'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# import-effects
|
|
38
|
+
|
|
39
|
+
> See what Python does when you `import`.
|
|
40
|
+
|
|
41
|
+
[](https://pypi.org/project/import-effects/) [](https://pypi.org/project/import-effects/) [](https://github.com/royalpinto007/import-effects/actions/workflows/ci.yml) [](LICENSE)
|
|
42
|
+
|
|
43
|
+
`import-effects` runs one import in a fresh child interpreter and reports file writes, sockets, subprocesses, threads, environment changes, and other observable import-time behavior.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install import-effects
|
|
47
|
+
import-effects requests
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+

|
|
51
|
+
|
|
52
|
+
## 30-second quickstart
|
|
53
|
+
|
|
54
|
+
```console
|
|
55
|
+
$ import-effects mypackage
|
|
56
|
+
import mypackage
|
|
57
|
+
|
|
58
|
+
✓ imported in 143 ms
|
|
59
|
+
|
|
60
|
+
SIDE EFFECTS
|
|
61
|
+
|
|
62
|
+
⚠ FILE WRITE
|
|
63
|
+
/home/me/.cache/mypackage/config.json
|
|
64
|
+
|
|
65
|
+
⚠ NETWORK
|
|
66
|
+
api.example.com:443
|
|
67
|
+
|
|
68
|
+
⚠ THREAD
|
|
69
|
+
background-worker
|
|
70
|
+
|
|
71
|
+
3 side effects detected · 1 file-write · 1 network · 1 thread
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The target must already be importable. `import-effects` never installs it and normal inspection needs no server, database, Docker, network access, or elevated privileges.
|
|
75
|
+
|
|
76
|
+
## CLI
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
import-effects package.submodule
|
|
80
|
+
import-effects requests --json
|
|
81
|
+
import-effects requests --quiet
|
|
82
|
+
import-effects requests --verbose
|
|
83
|
+
import-effects requests --timeout 10
|
|
84
|
+
import-effects requests --fail-on network,subprocess,file-write
|
|
85
|
+
import-effects requests --ignore '~/.cache/**'
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`--fail-on` turns selected observations into policy failures without pretending every observation is inherently unsafe. `--ignore` matches the human-readable effect detail and may be repeated.
|
|
89
|
+
|
|
90
|
+
Exit codes:
|
|
91
|
+
|
|
92
|
+
| Code | Meaning |
|
|
93
|
+
| ---: | ------------------------------------------------- |
|
|
94
|
+
| 0 | Import succeeded with no configured violation |
|
|
95
|
+
| 1 | A configured `--fail-on` effect was detected |
|
|
96
|
+
| 2 | Invalid CLI input |
|
|
97
|
+
| 3 | Target import failed or timed out |
|
|
98
|
+
| 4 | Internal inspector failure |
|
|
99
|
+
|
|
100
|
+
## Python API
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from import_effects import inspect_import
|
|
104
|
+
|
|
105
|
+
report = inspect_import("mypackage", timeout=10)
|
|
106
|
+
|
|
107
|
+
print(report.duration_ms)
|
|
108
|
+
for effect in report.effects:
|
|
109
|
+
print(effect.kind, effect.detail, effect.confidence)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
For tests:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from import_effects import assert_no_effects
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_import_stays_quiet() -> None:
|
|
119
|
+
assert_no_effects("mypackage", forbidden=("network", "subprocess", "file-write"))
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The models are frozen, typed dataclasses. Reports can be converted with `report.to_dict()` and filtered with `report.effects_of("network")`.
|
|
123
|
+
|
|
124
|
+
## What it observes
|
|
125
|
+
|
|
126
|
+
- files opened for writing, removal, rename, and directory operations
|
|
127
|
+
- socket connect, bind, and DNS activity
|
|
128
|
+
- subprocess spawning, `os.system`, and process forks
|
|
129
|
+
- Python threads and `multiprocessing` children started
|
|
130
|
+
- environment variable names added, removed, or changed, never their values
|
|
131
|
+
- current directory, logging handlers, `sys.path`, warning filters, and signal handlers
|
|
132
|
+
- newly imported module names, import duration, stdout/stderr, failures, crashes, and timeouts
|
|
133
|
+
|
|
134
|
+
CPython audit events provide high-confidence observations for many OS operations. Before/after snapshots and lifecycle wrappers cover state changes and thread/process starts. Each effect includes confidence and attribution metadata because an effect may come from the target, one of its dependencies, or code merely observed during the import window.
|
|
135
|
+
|
|
136
|
+
See [Architecture and limitations](docs/architecture.md) and the [API reference](docs/api.md).
|
|
137
|
+
|
|
138
|
+
## Security warning
|
|
139
|
+
|
|
140
|
+
**Importing untrusted Python code executes that code. `import-effects` is an observer, not a sandbox.**
|
|
141
|
+
|
|
142
|
+
The target runs in a separate child interpreter, so it cannot directly mutate the parent Python process. It still runs as your user with normal filesystem, network, and process permissions. Use an OS sandbox or disposable virtual machine when inspecting code you do not trust.
|
|
143
|
+
|
|
144
|
+
Target stdout and stderr are capped. Suspected credentials in captured messages are redacted, and environment variable values are never included. Audit hooks are visibility mechanisms, not a security boundary, and sufficiently hostile native code can evade or disable Python-level observation.
|
|
145
|
+
|
|
146
|
+
## Platform support
|
|
147
|
+
|
|
148
|
+
`import-effects` targets CPython 3.10 through 3.14 on Linux, macOS, and Windows. Audit event availability and process termination behavior differ by Python and OS. Linux currently provides the broadest coverage. Windows does not offer the same process-group cleanup guarantees as POSIX, and some native extensions perform operations below CPython's audit surface.
|
|
149
|
+
|
|
150
|
+
## Development
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
python -m venv .venv
|
|
154
|
+
. .venv/bin/activate
|
|
155
|
+
pip install -e '.[dev]'
|
|
156
|
+
ruff format --check .
|
|
157
|
+
ruff check .
|
|
158
|
+
mypy
|
|
159
|
+
pytest --cov
|
|
160
|
+
python -m build
|
|
161
|
+
twine check dist/*
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
See [Contributing](CONTRIBUTING.md), [Security](SECURITY.md), and the [Changelog](CHANGELOG.md).
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
MIT
|
|
169
|
+
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# import-effects
|
|
2
|
+
|
|
3
|
+
> See what Python does when you `import`.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/import-effects/) [](https://pypi.org/project/import-effects/) [](https://github.com/royalpinto007/import-effects/actions/workflows/ci.yml) [](LICENSE)
|
|
6
|
+
|
|
7
|
+
`import-effects` runs one import in a fresh child interpreter and reports file writes, sockets, subprocesses, threads, environment changes, and other observable import-time behavior.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install import-effects
|
|
11
|
+
import-effects requests
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+

|
|
15
|
+
|
|
16
|
+
## 30-second quickstart
|
|
17
|
+
|
|
18
|
+
```console
|
|
19
|
+
$ import-effects mypackage
|
|
20
|
+
import mypackage
|
|
21
|
+
|
|
22
|
+
✓ imported in 143 ms
|
|
23
|
+
|
|
24
|
+
SIDE EFFECTS
|
|
25
|
+
|
|
26
|
+
⚠ FILE WRITE
|
|
27
|
+
/home/me/.cache/mypackage/config.json
|
|
28
|
+
|
|
29
|
+
⚠ NETWORK
|
|
30
|
+
api.example.com:443
|
|
31
|
+
|
|
32
|
+
⚠ THREAD
|
|
33
|
+
background-worker
|
|
34
|
+
|
|
35
|
+
3 side effects detected · 1 file-write · 1 network · 1 thread
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The target must already be importable. `import-effects` never installs it and normal inspection needs no server, database, Docker, network access, or elevated privileges.
|
|
39
|
+
|
|
40
|
+
## CLI
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
import-effects package.submodule
|
|
44
|
+
import-effects requests --json
|
|
45
|
+
import-effects requests --quiet
|
|
46
|
+
import-effects requests --verbose
|
|
47
|
+
import-effects requests --timeout 10
|
|
48
|
+
import-effects requests --fail-on network,subprocess,file-write
|
|
49
|
+
import-effects requests --ignore '~/.cache/**'
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`--fail-on` turns selected observations into policy failures without pretending every observation is inherently unsafe. `--ignore` matches the human-readable effect detail and may be repeated.
|
|
53
|
+
|
|
54
|
+
Exit codes:
|
|
55
|
+
|
|
56
|
+
| Code | Meaning |
|
|
57
|
+
| ---: | ------------------------------------------------- |
|
|
58
|
+
| 0 | Import succeeded with no configured violation |
|
|
59
|
+
| 1 | A configured `--fail-on` effect was detected |
|
|
60
|
+
| 2 | Invalid CLI input |
|
|
61
|
+
| 3 | Target import failed or timed out |
|
|
62
|
+
| 4 | Internal inspector failure |
|
|
63
|
+
|
|
64
|
+
## Python API
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from import_effects import inspect_import
|
|
68
|
+
|
|
69
|
+
report = inspect_import("mypackage", timeout=10)
|
|
70
|
+
|
|
71
|
+
print(report.duration_ms)
|
|
72
|
+
for effect in report.effects:
|
|
73
|
+
print(effect.kind, effect.detail, effect.confidence)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
For tests:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from import_effects import assert_no_effects
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_import_stays_quiet() -> None:
|
|
83
|
+
assert_no_effects("mypackage", forbidden=("network", "subprocess", "file-write"))
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The models are frozen, typed dataclasses. Reports can be converted with `report.to_dict()` and filtered with `report.effects_of("network")`.
|
|
87
|
+
|
|
88
|
+
## What it observes
|
|
89
|
+
|
|
90
|
+
- files opened for writing, removal, rename, and directory operations
|
|
91
|
+
- socket connect, bind, and DNS activity
|
|
92
|
+
- subprocess spawning, `os.system`, and process forks
|
|
93
|
+
- Python threads and `multiprocessing` children started
|
|
94
|
+
- environment variable names added, removed, or changed, never their values
|
|
95
|
+
- current directory, logging handlers, `sys.path`, warning filters, and signal handlers
|
|
96
|
+
- newly imported module names, import duration, stdout/stderr, failures, crashes, and timeouts
|
|
97
|
+
|
|
98
|
+
CPython audit events provide high-confidence observations for many OS operations. Before/after snapshots and lifecycle wrappers cover state changes and thread/process starts. Each effect includes confidence and attribution metadata because an effect may come from the target, one of its dependencies, or code merely observed during the import window.
|
|
99
|
+
|
|
100
|
+
See [Architecture and limitations](docs/architecture.md) and the [API reference](docs/api.md).
|
|
101
|
+
|
|
102
|
+
## Security warning
|
|
103
|
+
|
|
104
|
+
**Importing untrusted Python code executes that code. `import-effects` is an observer, not a sandbox.**
|
|
105
|
+
|
|
106
|
+
The target runs in a separate child interpreter, so it cannot directly mutate the parent Python process. It still runs as your user with normal filesystem, network, and process permissions. Use an OS sandbox or disposable virtual machine when inspecting code you do not trust.
|
|
107
|
+
|
|
108
|
+
Target stdout and stderr are capped. Suspected credentials in captured messages are redacted, and environment variable values are never included. Audit hooks are visibility mechanisms, not a security boundary, and sufficiently hostile native code can evade or disable Python-level observation.
|
|
109
|
+
|
|
110
|
+
## Platform support
|
|
111
|
+
|
|
112
|
+
`import-effects` targets CPython 3.10 through 3.14 on Linux, macOS, and Windows. Audit event availability and process termination behavior differ by Python and OS. Linux currently provides the broadest coverage. Windows does not offer the same process-group cleanup guarantees as POSIX, and some native extensions perform operations below CPython's audit surface.
|
|
113
|
+
|
|
114
|
+
## Development
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
python -m venv .venv
|
|
118
|
+
. .venv/bin/activate
|
|
119
|
+
pip install -e '.[dev]'
|
|
120
|
+
ruff format --check .
|
|
121
|
+
ruff check .
|
|
122
|
+
mypy
|
|
123
|
+
pytest --cov
|
|
124
|
+
python -m build
|
|
125
|
+
twine check dist/*
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
See [Contributing](CONTRIBUTING.md), [Security](SECURITY.md), and the [Changelog](CHANGELOG.md).
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
MIT
|
|
133
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Reporting
|
|
4
|
+
|
|
5
|
+
Please report vulnerabilities privately through GitHub Security Advisories. Do not include live credentials, private package contents, or sensitive import reports in a public issue.
|
|
6
|
+
|
|
7
|
+
## Scope and safety model
|
|
8
|
+
|
|
9
|
+
`import-effects` executes the requested import in a child process. It is not a sandbox and does not make untrusted code safe. The child inherits the user's OS account permissions and a largely unchanged environment so ordinary imports continue to work.
|
|
10
|
+
|
|
11
|
+
The tool never installs packages. It redacts likely credentials from captured messages, never reports environment values, caps target output, validates module names, and applies a timeout. These controls reduce accidental disclosure and runaway imports but are not a security boundary against malicious Python or native code.
|
|
12
|
+
|
|
13
|
+
Supported security fixes target the latest released version.
|
|
14
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# API reference
|
|
2
|
+
|
|
3
|
+
## `inspect_import`
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
inspect_import(module: str, *, timeout: float = 10.0, python_executable: str | PathLike | None = None) -> ImportReport
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Runs the import in a fresh child interpreter. Invalid module names and timeouts less than or equal to zero raise `ValueError`. Failure to start or decode the child raises `InspectionError`. A target import exception is returned as a normal unsuccessful report.
|
|
10
|
+
|
|
11
|
+
## `assert_no_effects`
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
assert_no_effects(module: str, *, forbidden=("network", "subprocess", "file-write"), timeout=10.0) -> ImportReport
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Raises `AssertionError` when the import fails or a forbidden effect is observed. This helper works naturally in pytest, unittest, and other test runners without a plugin.
|
|
18
|
+
|
|
19
|
+
## Models
|
|
20
|
+
|
|
21
|
+
`ImportReport` and `Effect` are frozen dataclasses. `ImportReport.to_dict()` returns JSON-compatible data, `ImportReport.from_dict()` reconstructs a report, and `effects_of(*kinds)` filters effects.
|
|
22
|
+
|
|
23
|
+
An effect includes `kind`, redacted `detail`, optional `source`, `confidence`, `attribution`, and small non-sensitive metadata. Environment values are never included.
|
|
24
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Architecture and limitations
|
|
2
|
+
|
|
3
|
+
## Execution model
|
|
4
|
+
|
|
5
|
+
The public process validates the module name and launches the same Python interpreter with `python -m import_effects._probe`. The child prepares observers, takes a baseline snapshot, activates observation, calls `importlib.import_module`, takes a final snapshot, and writes a private JSON report. The parent enforces the timeout and deserializes that report into typed dataclasses.
|
|
6
|
+
|
|
7
|
+
Target stdout and stderr travel on separate pipes, so they cannot corrupt structured output. The parent never imports the target.
|
|
8
|
+
|
|
9
|
+
## Observation sources
|
|
10
|
+
|
|
11
|
+
CPython's [PEP 578 audit hooks](https://peps.python.org/pep-0578/) expose operations such as `open`, `socket.connect`, `subprocess.Popen`, and filesystem changes. Thread and multiprocessing start methods are observed with child-local wrappers. Snapshots compare environment keys, current directory, logging handlers, `sys.path`, warning filters, signal handlers, and loaded modules.
|
|
12
|
+
|
|
13
|
+
Startup work is excluded by activating observers immediately before the target import. Dependency activity during the import remains relevant and is reported. Stack inspection provides a best-effort source classification, not a security-grade provenance claim.
|
|
14
|
+
|
|
15
|
+
## Limitations
|
|
16
|
+
|
|
17
|
+
- This is not an OS sandbox. Target code executes with the child's normal permissions.
|
|
18
|
+
- Python audit hooks are designed for visibility and can be bypassed by hostile native code.
|
|
19
|
+
- A dependency imported by the target can cause any reported effect.
|
|
20
|
+
- Completed native threads or processes that do not use the observed Python APIs may be missed.
|
|
21
|
+
- Filesystem changes performed entirely below CPython's audit events may be missed.
|
|
22
|
+
- Snapshot-based findings show that state changed during the import window, not necessarily which exact line changed it.
|
|
23
|
+
- Import-generated bytecode cache writes are excluded as runtime noise.
|
|
24
|
+
- Timeout cleanup is strongest on POSIX. Windows child descendants may outlive a force-killed probe.
|
|
25
|
+
- Only modules already importable in the selected interpreter are inspected. No package installation occurs.
|
|
26
|
+
|
|
27
|
+
The report uses `target`, `dependency`, `observed-during-import`, and `runtime` attribution labels plus `high`, `medium`, or `low` confidence. Consumers should preserve those qualifiers.
|
|
28
|
+
|
|
Binary file
|