diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7daf0ba --- /dev/null +++ b/requirements.txt @@ -0,0 +1,31 @@ +nh3==0.2.18 +watchdog==4.0.2 +requests==2.32.3 +readme_renderer==44.0 +sqlparse==0.5.1 +urllib3==2.2.2 +Pygments==2.18.0 +zipp==3.19.2 +rfc3986==2.0.0 +asgiref==3.8.1 +mdurl==0.1.2 +pkginfo==1.10.0 +certifi==2024.7.4 +keyring==25.3.0 +twine==5.1.1 +charset-normalizer==3.3.2 +idna==3.7 +rich==13.7.1 +tzdata==2024.1 +setuptools==72.1.0 +-e git+https://github.com/CasualEngineerZombie/venvwatch.git@ce9773a53e4a8fdfe62a6d8ff6fef2f403817028#egg=venvwatch +requests-toolbelt==1.0.0 +jaraco.functools==4.0.2 +importlib_metadata==8.2.0 +pywin32-ctypes==0.2.2 +jaraco.classes==3.4.0 +more-itertools==10.4.0 +docutils==0.21.2 +markdown-it-py==3.0.0 +wheel==0.44.0 +jaraco.context==5.3.0 diff --git a/setup.py b/setup.py index 70918ec..dd34947 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='venvwatch', - version='0.5', + version='0.6', description='Automatically updates requirements.txt by watching your virtual environment for changes', long_description=open('README.md').read(), long_description_content_type='text/markdown', @@ -14,7 +14,7 @@ ], entry_points={ 'console_scripts': [ - 'venvwatch = venvwatch.main:main', + 'venvwatch = venvwatch.cli:main', # Changed from main to cli ], }, classifiers=[ @@ -26,4 +26,4 @@ 'Source': 'https://github.com/CasualEngineerZombie/venvwatch', 'Tracker': 'https://github.com/CasualEngineerZombie/venvwatch/issues', }, -) +) \ No newline at end of file diff --git a/venvwatch/cli.py b/venvwatch/cli.py new file mode 100644 index 0000000..ccab6d3 --- /dev/null +++ b/venvwatch/cli.py @@ -0,0 +1,16 @@ +import argparse +from .core import VenvWatch + +def main(): + parser = argparse.ArgumentParser(description="VenvWatch: Automatically update requirements.txt") + parser.add_argument('--venv', help="Path to the virtual environment") + parser.add_argument('--interval', type=int, default=5, help="Interval between checks (in seconds)") + parser.add_argument('--no-remove', action='store_true', help="Don't remove packages from requirements.txt") + + args = parser.parse_args() + + watcher = VenvWatch(venv_path=args.venv, interval=args.interval, no_remove=args.no_remove) + watcher.watch() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/venvwatch/core.py b/venvwatch/core.py new file mode 100644 index 0000000..1200780 --- /dev/null +++ b/venvwatch/core.py @@ -0,0 +1,54 @@ +import os +import subprocess +import time +import sys +from pathlib import Path + +class VenvWatch: + def __init__(self, venv_path=None, interval=5, no_remove=False): + self.venv_path = venv_path or os.environ.get('VIRTUAL_ENV') + self.interval = interval + self.no_remove = no_remove + self.requirements_file = 'requirements.txt' + self.last_packages = set() + + def get_pip_path(self): + if sys.platform == "win32": + return str(Path(self.venv_path) / "Scripts" / "pip.exe") + return str(Path(self.venv_path) / "bin" / "pip") + + def get_installed_packages(self): + pip_path = self.get_pip_path() + result = subprocess.run([pip_path, "freeze"], capture_output=True, text=True) + return set(result.stdout.splitlines()) + + def update_requirements(self): + current_packages = self.get_installed_packages() + + if current_packages != self.last_packages: + added = current_packages - self.last_packages + removed = self.last_packages - current_packages + + with open(self.requirements_file, 'w') as f: + for package in current_packages: + f.write(f"{package}\n") + + update_message = f"Updated {self.requirements_file}" + if added: + for package in added: + update_message += f"\n - Installed: {package}" + if removed and not self.no_remove: + for package in removed: + update_message += f"\n - Uninstalled: {package}" + + print(update_message) + + self.last_packages = current_packages + + def watch(self): + print(f"Watching virtual environment: {self.venv_path}") + print(f"Updating {self.requirements_file} every {self.interval} seconds") + + while True: + self.update_requirements() + time.sleep(self.interval) \ No newline at end of file diff --git a/venvwatch/main.py b/venvwatch/main.py deleted file mode 100644 index 95a8f4c..0000000 --- a/venvwatch/main.py +++ /dev/null @@ -1,42 +0,0 @@ -import os -import time -from watchdog.observers import Observer -from watchdog.events import FileSystemEventHandler -import importlib.metadata - -class PackageChangeHandler(FileSystemEventHandler): - def __init__(self, venv_path, requirements_file): - self.venv_path = venv_path - self.requirements_file = requirements_file - - def on_modified(self, event): - self.update_requirements() - - def update_requirements(self): - installed_packages = {pkg.metadata['Name']: pkg.metadata['Version'] for pkg in importlib.metadata.distributions()} - with open(self.requirements_file, 'w') as f: - for pkg, version in installed_packages.items(): - f.write(f"{pkg}=={version}\n") - -def start_watcher(venv_path, requirements_file): - event_handler = PackageChangeHandler(venv_path, requirements_file) - observer = Observer() - observer.schedule(event_handler, path=os.path.join(venv_path, 'lib'), recursive=True) - observer.start() - - try: - while True: - time.sleep(1) - except KeyboardInterrupt: - observer.stop() - observer.join() - -def main(): - import argparse - - parser = argparse.ArgumentParser(description="Watch your virtual environment and sync requirements.txt") - parser.add_argument('--venv', required=True, help="Path to the virtual environment") - parser.add_argument('--requirements', default='requirements.txt', help="Path to requirements.txt file") - args = parser.parse_args() - - start_watcher(args.venv, args.requirements) diff --git a/venvwatch/utils.py b/venvwatch/utils.py deleted file mode 100644 index e69de29..0000000