depvex 0.1.0__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.
- depvex-0.1.0/.github/workflows/publish.yml +36 -0
- depvex-0.1.0/PKG-INFO +10 -0
- depvex-0.1.0/README.md +0 -0
- depvex-0.1.0/depvex/__init__.py +0 -0
- depvex-0.1.0/depvex/__pycache__/__init__.cpython-311.pyc +0 -0
- depvex-0.1.0/depvex/__pycache__/cli.cpython-311.pyc +0 -0
- depvex-0.1.0/depvex/__pycache__/parser.cpython-311.pyc +0 -0
- depvex-0.1.0/depvex/__pycache__/resolver.cpython-311.pyc +0 -0
- depvex-0.1.0/depvex/__pycache__/watcher.cpython-311.pyc +0 -0
- depvex-0.1.0/depvex/cli.py +12 -0
- depvex-0.1.0/depvex/parser.py +23 -0
- depvex-0.1.0/depvex/resolver.py +155 -0
- depvex-0.1.0/depvex/watcher.py +36 -0
- depvex-0.1.0/depvex.egg-info/PKG-INFO +10 -0
- depvex-0.1.0/depvex.egg-info/SOURCES.txt +21 -0
- depvex-0.1.0/depvex.egg-info/dependency_links.txt +1 -0
- depvex-0.1.0/depvex.egg-info/entry_points.txt +2 -0
- depvex-0.1.0/depvex.egg-info/requires.txt +3 -0
- depvex-0.1.0/depvex.egg-info/top_level.txt +1 -0
- depvex-0.1.0/pyproject.toml +29 -0
- depvex-0.1.0/requirements.txt +4 -0
- depvex-0.1.0/setup.cfg +4 -0
- depvex-0.1.0/setup.py +15 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Publish Python Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-and-publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout repository
|
|
14
|
+
uses: actions/checkout@v4
|
|
15
|
+
with:
|
|
16
|
+
fetch-depth: 0
|
|
17
|
+
|
|
18
|
+
- name: Set up Python
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.11"
|
|
22
|
+
|
|
23
|
+
- name: Upgrade pip
|
|
24
|
+
run: python -m pip install --upgrade pip
|
|
25
|
+
|
|
26
|
+
- name: Install build tools
|
|
27
|
+
run: pip install build twine setuptools_scm
|
|
28
|
+
|
|
29
|
+
- name: Build package
|
|
30
|
+
run: python -m build
|
|
31
|
+
|
|
32
|
+
- name: Publish to PyPI
|
|
33
|
+
env:
|
|
34
|
+
TWINE_USERNAME: __token__
|
|
35
|
+
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
|
36
|
+
run: python -m twine upload --verbose dist/*
|
depvex-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: depvex
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Dependency and environment checker tool
|
|
5
|
+
Author: Shmuel Barda
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: requests==2.34.2
|
|
9
|
+
Requires-Dist: typer==0.26.8
|
|
10
|
+
Requires-Dist: watchdog==6.0.0
|
depvex-0.1.0/README.md
ADDED
|
File without changes
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from depvex.watcher import start_watching
|
|
3
|
+
app = typer.Typer()
|
|
4
|
+
|
|
5
|
+
@app.command()
|
|
6
|
+
def watch(path: str = "."):
|
|
7
|
+
"""Watch project and auto-update requirements.txt"""
|
|
8
|
+
print(f"[depvex] Watching {path} ...")
|
|
9
|
+
start_watching(path)
|
|
10
|
+
|
|
11
|
+
def main():
|
|
12
|
+
app()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
STD_LIB = set(getattr(sys, "stdlib_module_names", set())) | set(sys.builtin_module_names)
|
|
5
|
+
|
|
6
|
+
def extract_imports(code: str):
|
|
7
|
+
tree = ast.parse(code)
|
|
8
|
+
imports = set()
|
|
9
|
+
|
|
10
|
+
for node in ast.walk(tree):
|
|
11
|
+
if isinstance(node, ast.Import):
|
|
12
|
+
for n in node.names:
|
|
13
|
+
name = n.name.split(".")[0]
|
|
14
|
+
if name not in STD_LIB:
|
|
15
|
+
imports.add(name)
|
|
16
|
+
|
|
17
|
+
if isinstance(node, ast.ImportFrom):
|
|
18
|
+
if node.module:
|
|
19
|
+
name = node.module.split(".")[0]
|
|
20
|
+
if name not in STD_LIB:
|
|
21
|
+
imports.add(name)
|
|
22
|
+
|
|
23
|
+
return list(imports)
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
import subprocess
|
|
3
|
+
import urllib.request
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import requests
|
|
7
|
+
import time
|
|
8
|
+
|
|
9
|
+
from depvex.parser import extract_imports
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
CAPTIVE_PORTAL_URLS = [
|
|
13
|
+
"http://connectivitycheck.gstatic.com/generate_204",
|
|
14
|
+
"http://clients3.google.com/generate_204",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# -------------------------
|
|
19
|
+
# Internet check
|
|
20
|
+
# -------------------------
|
|
21
|
+
def internet_check(timeout=3):
|
|
22
|
+
for url in CAPTIVE_PORTAL_URLS:
|
|
23
|
+
try:
|
|
24
|
+
r = requests.get(url, timeout=timeout)
|
|
25
|
+
if r.status_code == 204:
|
|
26
|
+
return True
|
|
27
|
+
except requests.RequestException:
|
|
28
|
+
pass
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# -------------------------
|
|
33
|
+
# Local / installed check
|
|
34
|
+
# -------------------------
|
|
35
|
+
def is_installed(module_name):
|
|
36
|
+
return importlib.util.find_spec(module_name) is not None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# -------------------------
|
|
40
|
+
# Get local pip version
|
|
41
|
+
# -------------------------
|
|
42
|
+
def get_local_version(module_name):
|
|
43
|
+
try:
|
|
44
|
+
result = subprocess.check_output(
|
|
45
|
+
["pip", "show", module_name],
|
|
46
|
+
text=True
|
|
47
|
+
)
|
|
48
|
+
for line in result.splitlines():
|
|
49
|
+
if line.startswith("Version:"):
|
|
50
|
+
return line.split(":")[1].strip()
|
|
51
|
+
except:
|
|
52
|
+
return None
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# -------------------------
|
|
57
|
+
# Get PyPI version
|
|
58
|
+
# -------------------------
|
|
59
|
+
def get_pypi_version(module_name):
|
|
60
|
+
try:
|
|
61
|
+
url = f"https://pypi.org/pypi/{module_name}/json"
|
|
62
|
+
with urllib.request.urlopen(url, timeout=3) as r:
|
|
63
|
+
data = json.load(r)
|
|
64
|
+
return data["info"]["version"]
|
|
65
|
+
except:
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# -------------------------
|
|
70
|
+
# Resolve dependency
|
|
71
|
+
# -------------------------
|
|
72
|
+
def resolve(module_name, has_net):
|
|
73
|
+
version = get_local_version(module_name)
|
|
74
|
+
|
|
75
|
+
# installed locally → pin version
|
|
76
|
+
if version:
|
|
77
|
+
return f"{module_name}=={version}"
|
|
78
|
+
|
|
79
|
+
# not installed but internet → latest PyPI
|
|
80
|
+
if has_net:
|
|
81
|
+
v = get_pypi_version(module_name)
|
|
82
|
+
if v:
|
|
83
|
+
return f"{module_name}=={v}"
|
|
84
|
+
return module_name
|
|
85
|
+
|
|
86
|
+
# offline fallback
|
|
87
|
+
return module_name
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# -------------------------
|
|
91
|
+
# Write requirements file
|
|
92
|
+
# -------------------------
|
|
93
|
+
def write_req(lines, path="requirements.txt"):
|
|
94
|
+
with open(path, "w") as f:
|
|
95
|
+
for l in sorted(set(lines)):
|
|
96
|
+
f.write(l + "\n")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# -------------------------
|
|
100
|
+
# Rebuild requirements from project imports
|
|
101
|
+
# -------------------------
|
|
102
|
+
def rebuild_requirements(root=".", output_path=None):
|
|
103
|
+
discovered = set()
|
|
104
|
+
|
|
105
|
+
for dirpath, dirnames, filenames in os.walk(root):
|
|
106
|
+
dirnames[:] = [d for d in dirnames if d not in {".git", "__pycache__", ".venv", "venv", "node_modules"}]
|
|
107
|
+
|
|
108
|
+
for filename in filenames:
|
|
109
|
+
if not filename.endswith(".py"):
|
|
110
|
+
continue
|
|
111
|
+
|
|
112
|
+
file_path = os.path.join(dirpath, filename)
|
|
113
|
+
try:
|
|
114
|
+
with open(file_path, "r", encoding="utf-8") as handle:
|
|
115
|
+
discovered.update(extract_imports(handle.read()))
|
|
116
|
+
except (OSError, SyntaxError):
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
if output_path is None:
|
|
120
|
+
output_path = os.path.join(root, "requirements.txt")
|
|
121
|
+
|
|
122
|
+
req = []
|
|
123
|
+
has_net = internet_check()
|
|
124
|
+
for module_name in sorted(discovered):
|
|
125
|
+
if module_name:
|
|
126
|
+
req.append(resolve(module_name, has_net))
|
|
127
|
+
|
|
128
|
+
write_req(req, path=output_path)
|
|
129
|
+
return req
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# -------------------------
|
|
133
|
+
# Core monitor engine
|
|
134
|
+
# -------------------------
|
|
135
|
+
def monitor_project(module_list, interval=2):
|
|
136
|
+
last_req = None
|
|
137
|
+
|
|
138
|
+
while True:
|
|
139
|
+
has_net = internet_check()
|
|
140
|
+
|
|
141
|
+
req = []
|
|
142
|
+
|
|
143
|
+
for m in module_list:
|
|
144
|
+
if is_installed(m):
|
|
145
|
+
req.append(resolve(m, has_net))
|
|
146
|
+
|
|
147
|
+
if req != last_req:
|
|
148
|
+
print("\n[depvex] REQUIREMENTS UPDATED")
|
|
149
|
+
for r in req:
|
|
150
|
+
print(" ", r)
|
|
151
|
+
|
|
152
|
+
write_req(req)
|
|
153
|
+
last_req = req
|
|
154
|
+
|
|
155
|
+
time.sleep(interval)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from watchdog.observers import Observer
|
|
3
|
+
from watchdog.events import FileSystemEventHandler
|
|
4
|
+
|
|
5
|
+
from depvex.parser import extract_imports
|
|
6
|
+
from depvex.resolver import rebuild_requirements
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Handler(FileSystemEventHandler):
|
|
10
|
+
def __init__(self, root):
|
|
11
|
+
self.root = root
|
|
12
|
+
|
|
13
|
+
def on_modified(self, event):
|
|
14
|
+
if event.is_directory:
|
|
15
|
+
return
|
|
16
|
+
|
|
17
|
+
if event.src_path.endswith(".py"):
|
|
18
|
+
print(f"[depvex] change detected → full rescan")
|
|
19
|
+
rebuild_requirements(self.root)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def start_watching(path):
|
|
23
|
+
print("[depvex] watching:", path)
|
|
24
|
+
|
|
25
|
+
event_handler = Handler(path)
|
|
26
|
+
observer = Observer()
|
|
27
|
+
observer.schedule(event_handler, path, recursive=True)
|
|
28
|
+
observer.start()
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
while True:
|
|
32
|
+
time.sleep(1)
|
|
33
|
+
except KeyboardInterrupt:
|
|
34
|
+
observer.stop()
|
|
35
|
+
|
|
36
|
+
observer.join()
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: depvex
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Dependency and environment checker tool
|
|
5
|
+
Author: Shmuel Barda
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: requests==2.34.2
|
|
9
|
+
Requires-Dist: typer==0.26.8
|
|
10
|
+
Requires-Dist: watchdog==6.0.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
requirements.txt
|
|
4
|
+
setup.py
|
|
5
|
+
.github/workflows/publish.yml
|
|
6
|
+
depvex/__init__.py
|
|
7
|
+
depvex/cli.py
|
|
8
|
+
depvex/parser.py
|
|
9
|
+
depvex/resolver.py
|
|
10
|
+
depvex/watcher.py
|
|
11
|
+
depvex.egg-info/PKG-INFO
|
|
12
|
+
depvex.egg-info/SOURCES.txt
|
|
13
|
+
depvex.egg-info/dependency_links.txt
|
|
14
|
+
depvex.egg-info/entry_points.txt
|
|
15
|
+
depvex.egg-info/requires.txt
|
|
16
|
+
depvex.egg-info/top_level.txt
|
|
17
|
+
depvex/__pycache__/__init__.cpython-311.pyc
|
|
18
|
+
depvex/__pycache__/cli.cpython-311.pyc
|
|
19
|
+
depvex/__pycache__/parser.cpython-311.pyc
|
|
20
|
+
depvex/__pycache__/resolver.cpython-311.pyc
|
|
21
|
+
depvex/__pycache__/watcher.cpython-311.pyc
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
depvex
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel", "setuptools_scm"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
[project]
|
|
7
|
+
name = "depvex"
|
|
8
|
+
dynamic = ["version"]
|
|
9
|
+
description = "Dependency and environment checker tool"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Shmuel Barda" }
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
dependencies = [
|
|
18
|
+
"requests==2.34.2",
|
|
19
|
+
"typer==0.26.8",
|
|
20
|
+
"watchdog==6.0.0"
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.scripts]
|
|
24
|
+
depvex = "depvex.cli:main"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
[tool.setuptools_scm]
|
|
28
|
+
version_scheme = "release-branch-semver"
|
|
29
|
+
local_scheme = "no-local-version"
|
depvex-0.1.0/setup.cfg
ADDED
depvex-0.1.0/setup.py
ADDED