reyk 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.
- reyk-0.0.1/.gitignore +36 -0
- reyk-0.0.1/LICENSE +21 -0
- reyk-0.0.1/Makefile +46 -0
- reyk-0.0.1/PKG-INFO +76 -0
- reyk-0.0.1/README.md +51 -0
- reyk-0.0.1/pyproject.toml +129 -0
- reyk-0.0.1/reyk/__init__.py +0 -0
- reyk-0.0.1/reyk/caller_finder.py +52 -0
- reyk-0.0.1/reyk/isolator.py +46 -0
- reyk-0.0.1/reyk/stdlib_finder.py +28 -0
- reyk-0.0.1/reyk/vendor_importer.py +292 -0
- reyk-0.0.1/reyk/vendored_sys_modules.py +108 -0
- reyk-0.0.1/reyk/version.py +1 -0
- reyk-0.0.1/tests/__init__.py +0 -0
- reyk-0.0.1/tests/blackbox/__init__.py +0 -0
- reyk-0.0.1/tests/blackbox/conftest.py +81 -0
- reyk-0.0.1/tests/blackbox/example_project/__init__.py +0 -0
- reyk-0.0.1/tests/blackbox/example_project_file_manager.py +78 -0
- reyk-0.0.1/tests/blackbox/libraries_manager.py +33 -0
- reyk-0.0.1/tests/blackbox/predefined_tests/__init__.py +0 -0
- reyk-0.0.1/tests/blackbox/predefined_tests/import_path_extractor.py +6 -0
- reyk-0.0.1/tests/blackbox/predefined_tests/test_isolator_blackbox.py +523 -0
- reyk-0.0.1/tests/blackbox/predefined_tests/variable_access_statement_generator.py +129 -0
- reyk-0.0.1/tests/blackbox/project_paths.py +10 -0
- reyk-0.0.1/tests/blackbox/specific_libraries/__init__.py +0 -0
- reyk-0.0.1/tests/blackbox/specific_libraries/conftest.py +3 -0
- reyk-0.0.1/tests/blackbox/specific_libraries/pyproject.toml +21 -0
- reyk-0.0.1/tests/blackbox/specific_libraries/test_isolator_blackbox_specific_libraries.py +130 -0
- reyk-0.0.1/tests/blackbox/specific_libraries/uv.lock +1468 -0
- reyk-0.0.1/tests/blackbox/test_distributions_finder.py +42 -0
- reyk-0.0.1/tests/conftest.py +9 -0
- reyk-0.0.1/tests/internals/__init__.py +0 -0
- reyk-0.0.1/tests/internals/test_internals.py +43 -0
reyk-0.0.1/.gitignore
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Byte-compiled
|
|
2
|
+
__pycache__/
|
|
3
|
+
|
|
4
|
+
# Distribution / packaging
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
|
|
8
|
+
# Unit test / coverage reports
|
|
9
|
+
htmlcov/
|
|
10
|
+
.tox/
|
|
11
|
+
.nox/
|
|
12
|
+
.coverage
|
|
13
|
+
.coverage.*
|
|
14
|
+
.cache
|
|
15
|
+
nosetests.xml
|
|
16
|
+
coverage.xml
|
|
17
|
+
*.cover
|
|
18
|
+
*.py,cover
|
|
19
|
+
.hypothesis/
|
|
20
|
+
.pytest_cache/
|
|
21
|
+
coverage/
|
|
22
|
+
coverage.lcov
|
|
23
|
+
|
|
24
|
+
# Environments
|
|
25
|
+
.venv/
|
|
26
|
+
|
|
27
|
+
# PyPI configuration file
|
|
28
|
+
.pypirc
|
|
29
|
+
|
|
30
|
+
# Tests
|
|
31
|
+
tests/blackbox/locked_files_pending_deletion/
|
|
32
|
+
tests/blackbox/specific_libraries/test_libs
|
|
33
|
+
tests/blackbox/specific_libraries/vendor.lock
|
|
34
|
+
|
|
35
|
+
# Docs
|
|
36
|
+
.vercel
|
reyk-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Reyk's authors
|
|
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.
|
reyk-0.0.1/Makefile
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Can be installed on Windows with GNUWin32 or Chocolatey
|
|
2
|
+
|
|
3
|
+
specific_libraries_test := tests/blackbox/specific_libraries
|
|
4
|
+
specific_libraries_test_libs := $(specific_libraries_test)/test_libs
|
|
5
|
+
|
|
6
|
+
uv:
|
|
7
|
+
@uv -V || echo 'uv is not installed. Install via: https://docs.astral.sh/uv/getting-started/installation/'
|
|
8
|
+
|
|
9
|
+
install-hooks: uv
|
|
10
|
+
uv run pre-commit install --install-hooks
|
|
11
|
+
|
|
12
|
+
install: uv
|
|
13
|
+
uv sync --frozen --all-groups --all-extras --all-packages
|
|
14
|
+
|
|
15
|
+
install-test-libs: $(specific_libraries_test_libs)
|
|
16
|
+
|
|
17
|
+
$(specific_libraries_test_libs):
|
|
18
|
+
CLI_PATH="$$(pwd)/reyk-cli"; \
|
|
19
|
+
cd $(specific_libraries_test); \
|
|
20
|
+
uvx "$$CLI_PATH" sync
|
|
21
|
+
|
|
22
|
+
test-library: install install-test-libs
|
|
23
|
+
uv run coverage run $(COVERAGE_FLAGS) -m pytest -vv tests/
|
|
24
|
+
|
|
25
|
+
test-library-memray: install install-test-libs
|
|
26
|
+
uv run pytest -v tests/ --memray
|
|
27
|
+
|
|
28
|
+
test-cli: install install-test-libs
|
|
29
|
+
uv run coverage run $(COVERAGE_FLAGS) -m pytest -vv reyk-cli/tests/
|
|
30
|
+
|
|
31
|
+
# The --append flag is needed to combine the coverage data from both test-library and test-cli
|
|
32
|
+
test: install install-test-libs
|
|
33
|
+
mkdir -p coverage
|
|
34
|
+
rm -f coverage/.coverage.*
|
|
35
|
+
"$(MAKE)" COVERAGE_FLAGS=--append test-library
|
|
36
|
+
"$(MAKE)" COVERAGE_FLAGS=--append test-cli
|
|
37
|
+
|
|
38
|
+
testcov: test
|
|
39
|
+
uv run coverage report
|
|
40
|
+
@echo "building coverage lcov"
|
|
41
|
+
uv run coverage lcov
|
|
42
|
+
@echo "building coverage html"
|
|
43
|
+
uv run coverage html
|
|
44
|
+
|
|
45
|
+
update-dependencies: uv
|
|
46
|
+
uv lock --upgrade
|
reyk-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: reyk
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Run conflicting Python dependencies in one process — with vendored isolation.
|
|
5
|
+
Project-URL: Homepage, https://reyk.dev
|
|
6
|
+
Project-URL: Source, https://github.com/eyalohn/reyk
|
|
7
|
+
Author-email: Eyal Ohn <eyal.ohn@gmail.com>, Ofek Ashery <ofek.ashery@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: conflicting dependencies,dependency isolation,python import system,vendoring,virtual environment
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# Reyk
|
|
27
|
+
|
|
28
|
+
[](https://github.com/eyalohn/reyk/actions/workflows/test.yml)
|
|
29
|
+
[](https://coverage-badge.samuelcolvin.workers.dev/redirect/eyalohn/reyk)
|
|
30
|
+
[](https://pypi.python.org/pypi/reyk/)
|
|
31
|
+
[](https://pypi.python.org/pypi/reyk/)
|
|
32
|
+
[](https://reyk.dev/llms-full.txt)
|
|
33
|
+
|
|
34
|
+
Run conflicting Python dependencies in one process — with vendored isolation.
|
|
35
|
+
|
|
36
|
+
[Reyk](https://reyk.dev) lets you **vendor Python dependencies into your project and isolate them by design**, without changing how you write imports. We leverage Python's import system internals to achieve transparent dependency isolation.
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
- **Zero Configuration**: Enable isolation with a single function call
|
|
41
|
+
- **Dependency Isolation**: Avoid conflicts by shipping libraries with your package
|
|
42
|
+
- **Version Flexibility**: Run multiple versions of the same library in one process
|
|
43
|
+
- **Transparent Imports**: Keep your public imports unchanged (`import requests` still works)
|
|
44
|
+
- **Lightweight**: Minimal overhead with a tiny import hook
|
|
45
|
+
|
|
46
|
+
# Installation
|
|
47
|
+
|
|
48
|
+
You can install Reyk from PyPI:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
uv add reyk
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Enable isolation from your package's `__init__.py`:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from reyk.isolator import isolate_package
|
|
58
|
+
|
|
59
|
+
isolate_package()
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Then, vendor your dependencies with Reyk's CLI:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
uvx reyk-cli add requests==2.25.1
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**That's it!** Inside `your_package`, `import requests` resolves to `your_package.libs.requests`. Outside `your_package`, `import requests` continues to resolve to the global installation.
|
|
69
|
+
|
|
70
|
+
## Documentation
|
|
71
|
+
|
|
72
|
+
For detailed documentation, examples, and API reference, visit the documentation site at https://reyk.dev.
|
|
73
|
+
|
|
74
|
+
## Limitations
|
|
75
|
+
|
|
76
|
+
- Multi-threaded applications are not supported.
|
reyk-0.0.1/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Reyk
|
|
2
|
+
|
|
3
|
+
[](https://github.com/eyalohn/reyk/actions/workflows/test.yml)
|
|
4
|
+
[](https://coverage-badge.samuelcolvin.workers.dev/redirect/eyalohn/reyk)
|
|
5
|
+
[](https://pypi.python.org/pypi/reyk/)
|
|
6
|
+
[](https://pypi.python.org/pypi/reyk/)
|
|
7
|
+
[](https://reyk.dev/llms-full.txt)
|
|
8
|
+
|
|
9
|
+
Run conflicting Python dependencies in one process — with vendored isolation.
|
|
10
|
+
|
|
11
|
+
[Reyk](https://reyk.dev) lets you **vendor Python dependencies into your project and isolate them by design**, without changing how you write imports. We leverage Python's import system internals to achieve transparent dependency isolation.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Zero Configuration**: Enable isolation with a single function call
|
|
16
|
+
- **Dependency Isolation**: Avoid conflicts by shipping libraries with your package
|
|
17
|
+
- **Version Flexibility**: Run multiple versions of the same library in one process
|
|
18
|
+
- **Transparent Imports**: Keep your public imports unchanged (`import requests` still works)
|
|
19
|
+
- **Lightweight**: Minimal overhead with a tiny import hook
|
|
20
|
+
|
|
21
|
+
# Installation
|
|
22
|
+
|
|
23
|
+
You can install Reyk from PyPI:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
uv add reyk
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Enable isolation from your package's `__init__.py`:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from reyk.isolator import isolate_package
|
|
33
|
+
|
|
34
|
+
isolate_package()
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Then, vendor your dependencies with Reyk's CLI:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uvx reyk-cli add requests==2.25.1
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**That's it!** Inside `your_package`, `import requests` resolves to `your_package.libs.requests`. Outside `your_package`, `import requests` continues to resolve to the global installation.
|
|
44
|
+
|
|
45
|
+
## Documentation
|
|
46
|
+
|
|
47
|
+
For detailed documentation, examples, and API reference, visit the documentation site at https://reyk.dev.
|
|
48
|
+
|
|
49
|
+
## Limitations
|
|
50
|
+
|
|
51
|
+
- Multi-threaded applications are not supported.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "reyk"
|
|
3
|
+
description = "Run conflicting Python dependencies in one process — with vendored isolation."
|
|
4
|
+
authors = [
|
|
5
|
+
{ name = 'Eyal Ohn', email = 'eyal.ohn@gmail.com' },
|
|
6
|
+
{ name = 'Ofek Ashery', email = 'ofek.ashery@gmail.com' },
|
|
7
|
+
]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
license = 'MIT'
|
|
10
|
+
license-files = ['LICENSE']
|
|
11
|
+
keywords = [
|
|
12
|
+
"vendoring",
|
|
13
|
+
"dependency isolation",
|
|
14
|
+
"conflicting dependencies",
|
|
15
|
+
"virtual environment",
|
|
16
|
+
"python import system",
|
|
17
|
+
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
'Development Status :: 4 - Beta',
|
|
20
|
+
'Programming Language :: Python',
|
|
21
|
+
'Programming Language :: Python :: Implementation :: CPython',
|
|
22
|
+
'Programming Language :: Python :: 3',
|
|
23
|
+
'Programming Language :: Python :: 3.9',
|
|
24
|
+
'Programming Language :: Python :: 3.10',
|
|
25
|
+
'Programming Language :: Python :: 3.11',
|
|
26
|
+
'Programming Language :: Python :: 3.12',
|
|
27
|
+
'Programming Language :: Python :: 3.13',
|
|
28
|
+
'Programming Language :: Python :: 3.14',
|
|
29
|
+
'Intended Audience :: Developers',
|
|
30
|
+
'Operating System :: OS Independent',
|
|
31
|
+
]
|
|
32
|
+
requires-python = ">=3.9"
|
|
33
|
+
dependencies = []
|
|
34
|
+
dynamic = ["version"]
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Homepage = "https://reyk.dev"
|
|
38
|
+
Source = "https://github.com/eyalohn/reyk"
|
|
39
|
+
|
|
40
|
+
[build-system]
|
|
41
|
+
requires = ["hatchling"]
|
|
42
|
+
build-backend = "hatchling.build"
|
|
43
|
+
|
|
44
|
+
[dependency-groups]
|
|
45
|
+
dev = ["coverage[toml]>=7.10.7", "pre-commit>=4.3.0", "pytest>=8.4.2"]
|
|
46
|
+
testing-extra = [
|
|
47
|
+
"pytest-memray>=1.8.0; platform_python_implementation == 'CPython' and platform_system != 'Windows'",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[tool.hatch.version]
|
|
51
|
+
path = "reyk/version.py"
|
|
52
|
+
|
|
53
|
+
[tool.hatch.build.targets.sdist]
|
|
54
|
+
include = [
|
|
55
|
+
"/README.md",
|
|
56
|
+
"/Makefile",
|
|
57
|
+
"/reyk",
|
|
58
|
+
"/tests",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[tool.uv.workspace]
|
|
62
|
+
members = ["reyk-cli"]
|
|
63
|
+
|
|
64
|
+
[tool.ruff]
|
|
65
|
+
line-length = 120
|
|
66
|
+
exclude = ["test_libs"]
|
|
67
|
+
|
|
68
|
+
[tool.ruff.lint]
|
|
69
|
+
select = ["ALL"]
|
|
70
|
+
ignore = [
|
|
71
|
+
"W293",
|
|
72
|
+
"D101",
|
|
73
|
+
"D102",
|
|
74
|
+
"D103",
|
|
75
|
+
"D104",
|
|
76
|
+
"D105",
|
|
77
|
+
"D100",
|
|
78
|
+
"T201",
|
|
79
|
+
"I001",
|
|
80
|
+
"INP001",
|
|
81
|
+
"EM102",
|
|
82
|
+
"TRY003",
|
|
83
|
+
"S101",
|
|
84
|
+
"FA102",
|
|
85
|
+
"UP036",
|
|
86
|
+
"EM101",
|
|
87
|
+
"G004",
|
|
88
|
+
"D107",
|
|
89
|
+
"FA100",
|
|
90
|
+
"COM812",
|
|
91
|
+
"TC006",
|
|
92
|
+
"SIM118",
|
|
93
|
+
"D205",
|
|
94
|
+
"D212",
|
|
95
|
+
"D401",
|
|
96
|
+
"D400",
|
|
97
|
+
"D415",
|
|
98
|
+
"UP040",
|
|
99
|
+
"D203",
|
|
100
|
+
"TC003",
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
[tool.ruff.lint.per-file-ignores]
|
|
104
|
+
"tests/**/test_*.py" = ["SLF001", "PLC0415", "F401"]
|
|
105
|
+
"tests/**/conftest.py" = ["SLF001", "PLC0415", "F401"]
|
|
106
|
+
"tests/blackbox/distributions_finder.py" = ["SLF001", "PLC0415", "F401"]
|
|
107
|
+
"reyk/vendor_importer.py" = ["A002"]
|
|
108
|
+
|
|
109
|
+
[tool.pyright]
|
|
110
|
+
include = ["reyk", "tests"]
|
|
111
|
+
exclude = ["tests/blackbox/specific_libraries/test_libs", ".venv"]
|
|
112
|
+
venvPath = "."
|
|
113
|
+
venv = ".venv"
|
|
114
|
+
|
|
115
|
+
[tool.codespell]
|
|
116
|
+
skip = ".git,env*,uv.lock,**/package-lock.json,./tests/blackbox/specific_libraries/test_libs,node_modules,htmlcov"
|
|
117
|
+
ignore-words-list = ["reyk"]
|
|
118
|
+
|
|
119
|
+
[tool.coverage.run]
|
|
120
|
+
source = ["reyk", "reyk-cli/src"]
|
|
121
|
+
relative_files = true
|
|
122
|
+
branch = true
|
|
123
|
+
data_file = "coverage/.coverage"
|
|
124
|
+
|
|
125
|
+
[tool.coverage.lcov]
|
|
126
|
+
output = "coverage/coverage.lcov"
|
|
127
|
+
|
|
128
|
+
[tool.coverage.html]
|
|
129
|
+
directory = "coverage/htmlcov/coverage.lcov"
|
|
File without changes
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
from collections.abc import Iterator
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from reyk.stdlib_finder import is_part_of_stdlib
|
|
8
|
+
|
|
9
|
+
LOGGER = logging.getLogger(__name__)
|
|
10
|
+
MY_PACKAGE_NAME = __package__
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class NoCallerOutsideLibFoundError(RuntimeError): ...
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class StackFrame:
|
|
18
|
+
filename: Path
|
|
19
|
+
module_name: str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_caller_frame_outside_reyk() -> StackFrame:
|
|
23
|
+
for frame in _iterate_over_stack():
|
|
24
|
+
if is_part_of_stdlib(frame.module_name):
|
|
25
|
+
continue
|
|
26
|
+
|
|
27
|
+
if frame.module_name == MY_PACKAGE_NAME or frame.module_name.startswith(f"{MY_PACKAGE_NAME}."):
|
|
28
|
+
# This function
|
|
29
|
+
continue
|
|
30
|
+
|
|
31
|
+
return frame
|
|
32
|
+
|
|
33
|
+
raise NoCallerOutsideLibFoundError("Failed to find caller outside builtin")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _iterate_over_stack() -> Iterator[StackFrame]:
|
|
37
|
+
current_frame = sys._getframe(1) # noqa: SLF001
|
|
38
|
+
while current_frame is not None:
|
|
39
|
+
yield StackFrame(
|
|
40
|
+
filename=Path(current_frame.f_code.co_filename),
|
|
41
|
+
module_name=current_frame.f_globals["__name__"],
|
|
42
|
+
)
|
|
43
|
+
current_frame = current_frame.f_back
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def is_caller_part_of_library(library_name: str) -> bool:
|
|
47
|
+
try:
|
|
48
|
+
caller_frame = get_caller_frame_outside_reyk()
|
|
49
|
+
except NoCallerOutsideLibFoundError:
|
|
50
|
+
return False
|
|
51
|
+
caller_module_name = caller_frame.module_name
|
|
52
|
+
return (caller_module_name == library_name) or (caller_module_name.startswith(f"{library_name}."))
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import builtins
|
|
2
|
+
import importlib
|
|
3
|
+
import logging
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional, cast
|
|
6
|
+
|
|
7
|
+
from reyk.caller_finder import get_caller_frame_outside_reyk
|
|
8
|
+
from reyk.vendor_importer import BuiltinsImporter, VendorImporter
|
|
9
|
+
|
|
10
|
+
LOGGER = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def isolate_package(package_path: Optional[Path] = None, vendorized_libs_directory_import_path: str = "libs") -> None:
|
|
14
|
+
vendor_importer = create_vendor_importer(package_path, vendorized_libs_directory_import_path)
|
|
15
|
+
LOGGER.debug(f"Isolating library: {vendor_importer.package_name} ({vendorized_libs_directory_import_path=})")
|
|
16
|
+
vendor_importer.install()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def create_vendor_importer(
|
|
20
|
+
package_path: Optional[Path] = None,
|
|
21
|
+
vendorized_libs_directory_import_path: str = "libs",
|
|
22
|
+
) -> VendorImporter:
|
|
23
|
+
"""
|
|
24
|
+
Isolates a package dependencies - everything imported from the specified package_path
|
|
25
|
+
will prefer to import libraries inside the specified vendorized_libs_directory_import_path
|
|
26
|
+
instead of the default PYTHONPATH/site-packages.
|
|
27
|
+
The package_path can be unspecified/None to default to the caller's parent package.
|
|
28
|
+
vendorized_libs_directory_import_path should be the path to import the dependencies
|
|
29
|
+
from the package.
|
|
30
|
+
For example if the dependencies are in 'libs' it should be 'libs' as well as we need to perform:
|
|
31
|
+
`import libs` but if it's a subdirectory like 'my/libs' it should be 'my.libs' for
|
|
32
|
+
`import my.libs`.
|
|
33
|
+
"""
|
|
34
|
+
if package_path is None:
|
|
35
|
+
package_path = get_caller_frame_outside_reyk().filename.parent
|
|
36
|
+
|
|
37
|
+
package_name = package_path.name
|
|
38
|
+
vendorized_libs_path = package_path / vendorized_libs_directory_import_path
|
|
39
|
+
return VendorImporter(
|
|
40
|
+
package_name,
|
|
41
|
+
vendorized_libs_directory_import_path,
|
|
42
|
+
vendorized_libs_path,
|
|
43
|
+
# Cast as for some reason the definition of __import__ thinks fromlist is not nullable
|
|
44
|
+
cast(BuiltinsImporter, builtins.__import__),
|
|
45
|
+
importlib.import_module,
|
|
46
|
+
)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from functools import cache
|
|
5
|
+
from collections.abc import Iterator
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@cache
|
|
9
|
+
def list_stdlib_modules() -> list[str]:
|
|
10
|
+
return list(_list_stdlib_modules_iter())
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _list_stdlib_modules_iter() -> Iterator[str]:
|
|
14
|
+
standard_lib_dir = Path(os.__file__).parent
|
|
15
|
+
for std_lib_file in standard_lib_dir.iterdir():
|
|
16
|
+
if _is_package(std_lib_file) or (std_lib_file.suffix == ".py"):
|
|
17
|
+
yield std_lib_file.stem
|
|
18
|
+
|
|
19
|
+
yield from sys.builtin_module_names
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _is_package(path: Path) -> bool:
|
|
23
|
+
return path.is_dir() and (path / "__init__.py").exists()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def is_part_of_stdlib(module_name: str) -> bool:
|
|
27
|
+
library_name, _, _ = module_name.partition(".")
|
|
28
|
+
return library_name in list_stdlib_modules()
|