Skip to main content
Glama
dependency_checker.py7.6 kB
# freecad-ai/utils/dependency_checker.py import importlib import subprocess import sys import os # It's good practice to define requirements in a central place # For example, read from freecad-ai/requirements.txt ADDON_REQUIREMENTS_FILE = os.path.join( os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "requirements.txt" ) class DependencyChecker: def __init__(self): self.required_packages = self._parse_requirements() def _parse_requirements(self): """Parses the requirements.txt file for the addon.""" packages = [] try: with open(ADDON_REQUIREMENTS_FILE, "r", encoding="utf-8") as f: for line in f: line = line.strip() if line and not line.startswith("#"): # Basic parsing, might need to be more robust for version specifiers, etc. package_name = line.split(">=")[0].split("==")[0].split("<=")[0].strip() packages.append(package_name) except FileNotFoundError: print(f"Error: {ADDON_REQUIREMENTS_FILE} not found.") return packages def get_missing_dependencies(self): """Checks for missing dependencies.""" missing = [] for package_spec in self.required_packages: try: # For packages with hyphens in name, Python import name might use underscores import_name = package_spec.replace("-", "_") importlib.import_module(import_name) except ImportError: missing.append(package_spec) except (TypeError, AttributeError) as e: # Handle Protocol-related errors and other type inspection issues if "Protocols" in str(e) or "issubclass" in str(e): # Package is installed but has Protocol/typing issues, skip it continue else: # Some other error, treat as missing missing.append(package_spec) except Exception as e: # Handle any other unexpected errors during import print(f"Warning: Unexpected error checking {package_spec}: {e}") missing.append(package_spec) return missing def install_dependencies(self, dependencies_to_install: list[str]) -> tuple[list[str], list[str]]: """ Installs a list of dependencies using pip with FreeCAD compatibility. Returns a tuple containing two lists: successfully installed packages and failed packages. """ if not dependencies_to_install: return [], [] successfully_installed: list[str] = [] failed_to_install: list[str] = [] for package in dependencies_to_install: print(f"Installing {package}...") installation_success = False # Method 1: Try programmatic pip (most FreeCAD-compatible) try: import pip pip_args = ["install", package] # Try different pip interfaces if hasattr(pip, 'main'): # Older pip versions result = pip.main(pip_args) if result == 0: installation_success = True successfully_installed.append(package) print(f"✅ Successfully installed {package} (pip.main)") elif hasattr(pip, '_internal'): # Newer pip versions from pip._internal import main as pip_main result = pip_main(pip_args) if result == 0: installation_success = True successfully_installed.append(package) print(f"✅ Successfully installed {package} (pip._internal)") except Exception as e: print(f"⚠️ Programmatic pip failed for {package}: {e}") # Method 2: Try direct pip executable (if programmatic failed) if not installation_success: try: import shutil pip_executable = shutil.which("pip") or shutil.which("pip3") if pip_executable: pip_command = [pip_executable, "install", package] # Run the command process = subprocess.Popen( pip_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding='utf-8' ) stdout, stderr = process.communicate() if process.returncode == 0: installation_success = True successfully_installed.append(package) print(f"✅ Successfully installed {package} (direct pip)") else: print(f"⚠️ Direct pip failed for {package}: {stderr}") else: print(f"⚠️ Direct pip executable not found for {package}") except Exception as e: print(f"⚠️ Direct pip approach failed for {package}: {e}") # Method 3: Try subprocess with python -c (avoid -m flag) if not installation_success: try: # Build pip install command as Python code to avoid -m flag pip_code = f"import pip; pip.main(['install', '{package}'])" process = subprocess.Popen( [sys.executable, "-c", pip_code], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding='utf-8' ) stdout, stderr = process.communicate() if process.returncode == 0: installation_success = True successfully_installed.append(package) print(f"✅ Successfully installed {package} (subprocess)") else: print(f"⚠️ Subprocess approach failed for {package}: {stderr}") except Exception as e: print(f"⚠️ Subprocess approach failed for {package}: {e}") # If all methods failed if not installation_success: failed_to_install.append(package) print(f"❌ All installation methods failed for {package}") return successfully_installed, failed_to_install if __name__ == '__main__': checker = DependencyChecker() missing_deps = checker.get_missing_dependencies() if missing_deps: print(f"Missing dependencies: {missing_deps}") current_missing_deps = missing_deps success, failure = checker.install_dependencies([current_missing_deps[0]]) print(f"Successfully installed: {success}") print(f"Failed to install: {failure}") else: print("All dependencies are satisfied!")

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jango-blockchained/mcp-freecad'

If you have feedback or need assistance with the MCP directory API, please join our Discord server