Skip to main content
Glama
python.ts9.44 kB
import { LanguageResolver, DependencyInfo, ProjectFiles } from "./types"; // Python built-in modules (comprehensive list based on Python 3.12+ documentation) // This includes the standard library modules that come with Python const PYTHON_BUILTINS = new Set([ // Built-in types and functions "__main__", "__future__", "_thread", "abc", "argparse", "array", "ast", "asyncio", "atexit", "base64", "bdb", "binascii", "bisect", "builtins", "bz2", "calendar", "cmath", "cmd", "code", "codecs", "codeop", "collections", "colorsys", "compileall", "concurrent", "configparser", "contextlib", "contextvars", "copy", "copyreg", "csv", "ctypes", "curses", "dataclasses", "datetime", "dbm", "decimal", "difflib", "dis", "doctest", "email", "encodings", "ensurepip", "enum", "errno", "faulthandler", "fcntl", "filecmp", "fileinput", "fnmatch", "fractions", "ftplib", "functools", "gc", "getopt", "getpass", "gettext", "glob", "graphlib", "grp", "gzip", "hashlib", "heapq", "hmac", "html", "http", "idlelib", "imaplib", "importlib", "inspect", "io", "ipaddress", "itertools", "json", "keyword", "linecache", "locale", "logging", "lzma", "mailbox", "marshal", "math", "mimetypes", "mmap", "modulefinder", "msvcrt", "multiprocessing", "netrc", "numbers", "operator", "optparse", "os", "pathlib", "pdb", "pickle", "pickletools", "pkgutil", "platform", "plistlib", "poplib", "posix", "pprint", "profile", "pstats", "pty", "pwd", "py_compile", "pyclbr", "pydoc", "queue", "quopri", "random", "re", "readline", "reprlib", "resource", "rlcompleter", "runpy", "sched", "secrets", "select", "selectors", "shelve", "shlex", "shutil", "signal", "site", "sitecustomize", "smtplib", "socket", "socketserver", "sqlite3", "ssl", "stat", "statistics", "string", "stringprep", "struct", "subprocess", "symtable", "sys", "sysconfig", "syslog", "tabnanny", "tarfile", "tempfile", "termios", "test", "textwrap", "threading", "time", "timeit", "tkinter", "tomllib", "trace", "traceback", "tracemalloc", "turtle", "types", "typing", "unicodedata", "unittest", "urllib", "uuid", "venv", "warnings", "wave", "weakref", "webbrowser", "winreg", "winsound", "wsgiref", "xml", "xmlrpc", "zipapp", "zipfile", "zipimport", "zlib", "zoneinfo", // Common submodules that might be imported directly "collections.abc", "concurrent.futures", "email.mime", "html.parser", "http.client", "http.server", "importlib.metadata", "importlib.resources", "json.tool", "logging.config", "logging.handlers", "multiprocessing.pool", "os.path", "urllib.parse", "urllib.request", "xml.etree", "xml.etree.ElementTree", ]); export class PythonResolver implements LanguageResolver { /** * Extract package names from Python import statements */ extractImports(code: string): string[] { const imports = new Set<string>(); // Regex patterns for different Python import styles const patterns = [ // import module /^import\s+([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)/gm, // from module import ... /^from\s+([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)\s+import/gm, ]; for (const pattern of patterns) { let match; while ((match = pattern.exec(code)) !== null) { const moduleName = match[1]; // Skip relative imports (starting with .) if (moduleName.startsWith(".")) { continue; } // Get the top-level package name const topLevelPackage = moduleName.split(".")[0]; // Skip Python built-in modules if ( !PYTHON_BUILTINS.has(topLevelPackage) && !PYTHON_BUILTINS.has(moduleName) ) { imports.add(topLevelPackage); } } } return Array.from(imports); } /** * Lookup package version from PyPI registry */ async lookupPackageVersion(packageName: string): Promise<string> { try { const response = await fetch(`https://pypi.org/pypi/${packageName}/json`); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const data = await response.json(); return `>=${data.info.version}`; } catch (error) { console.warn(`Failed to lookup version for ${packageName}:`, error); // Fallback to latest return "*"; } } /** * Resolve all dependencies for a Python file */ async resolveDependencies( code: string, providedDependencies?: Record<string, string>, ): Promise<DependencyInfo> { // Discover dependencies from import statements const discoveredPackages = this.extractImports(code); const providedPackageNames = new Set( Object.keys(providedDependencies || {}), ); // Lookup versions for each package (skip if already provided) const dependencies: Record<string, string> = {}; for (const pkg of discoveredPackages) { if (providedPackageNames.has(pkg)) { // Skip PyPI lookup if user already provided this package version console.log( `Skipping PyPI lookup for "${pkg}" - version provided by user`, ); continue; } try { dependencies[pkg] = await this.lookupPackageVersion(pkg); } catch (error) { console.warn(`Failed to resolve ${pkg}, using latest:`, error); dependencies[pkg] = "*"; } } return { discoveredPackages, dependencies }; } /** * Generate Python project configuration files */ generateProjectFiles( filename: string, entrypointRelPath: string, dependencies: Record<string, string>, ): ProjectFiles { const projectName = filename .replace(/\.py$/, "") .replace(/[^a-zA-Z0-9_-]/g, "_"); // Helper function to ensure proper version constraint formatting const formatDependency = (pkg: string, version: string): string => { // If version already has a constraint operator, use it as-is if (/^[><=~!]/.test(version) || version === "*") { return `${pkg}${version}`; } // If it's a bare version number, add >= prefix if (/^\d+\.\d+/.test(version)) { return `${pkg}>=${version}`; } // Default fallback return `${pkg}${version}`; }; const pyprojectToml = `[build-system] requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta" [project] name = "${projectName}" version = "1.0.0" description = "Kernel Python app" readme = "README.md" requires-python = ">=3.8" dependencies = [ ${Object.entries(dependencies) .map(([pkg, version]) => ` "${formatDependency(pkg, version)}",`) .join("\n")} ] `; // Generate a simple README const readme = `# ${projectName} A Python app deployed to Kernel. ## Installation \`\`\`bash pip install -e . \`\`\` ## Usage \`\`\`bash python ${entrypointRelPath} \`\`\` `; return { "pyproject.toml": pyprojectToml, "README.md": readme, }; } /** * Merge auto-discovered dependencies with provided dependencies * Provided dependencies take priority over auto-discovered ones * Preserves exact formatting of provided dependencies */ mergeDependencies( autoDependencies: Record<string, string>, providedDependencies?: Record<string, string>, ): Record<string, string> { const normalizedProvidedDeps: Record<string, string> = providedDependencies || {}; // Helper function to ensure version constraint for auto-discovered deps only const ensureVersionConstraint = (version: string): string => { // If version already has a constraint operator, return as-is if (/^[><=~!]/.test(version) || version === "*") { return version; } // If it's a bare version number, add >= prefix for auto-discovered deps if (/^\d+\.\d+/.test(version)) { return `>=${version}`; } // Default fallback return version; }; // Validate provided dependencies and warn about potential issues Object.entries(normalizedProvidedDeps).forEach(([pkg, version]) => { if (/^\d+\.\d+/.test(version) && !/^[><=~!]/.test(version)) { console.warn( `Warning: Package "${pkg}": "${version}" appears to be a bare version number. ` + `Python pip expects version constraints like ">=1.0.0", "==1.0.0", "~=1.0.0", etc. ` + `If deployment fails, ensure your version matches your pyproject.toml exactly.`, ); } }); const finalDependencies: Record<string, string> = {}; // Add auto-discovered dependencies with constraints Object.entries(autoDependencies).forEach(([pkg, version]) => { // Only apply auto-constraints if this package isn't overridden by provided deps if (!normalizedProvidedDeps.hasOwnProperty(pkg)) { finalDependencies[pkg] = ensureVersionConstraint(version); } }); // Add provided dependencies EXACTLY as specified (no modification) Object.entries(normalizedProvidedDeps).forEach(([pkg, version]) => { finalDependencies[pkg] = version; // Preserve exact format from provided deps }); return finalDependencies; } }

Latest Blog Posts

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/onkernel/kernel-mcp-server'

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