A Python environment detective tool that reports package versions and hardware resources.
—
Utility functions for package version detection, version comparison, and environment detection. These functions provide the building blocks used by the reporting system.
MODULE_NOT_FOUND = 'Module not found'
MODULE_TROUBLE = 'Trouble importing'
VERSION_NOT_FOUND = 'Version unknown'Get version information for any installed package using multiple detection strategies.
def get_version(module: str | ModuleType) -> tuple[str, str | None]:
"""
Get the version of a module by passing the package or its name.
This function tries multiple strategies to detect version information:
1. importlib.metadata (preferred)
2. Common version attributes (__version__, version)
3. Known package-specific version locations
4. Package-specific version methods
Parameters:
- module (str | ModuleType): Name of module to import or the module itself
Returns:
- tuple[str, str | None]: (package_name, version_string)
- version_string can be None if version cannot be determined
- Returns MODULE_NOT_FOUND if module cannot be imported
- Returns MODULE_TROUBLE if module import fails with error
- Returns VERSION_NOT_FOUND if module loads but version cannot be found
"""Compare version strings to check if a package meets minimum version requirements.
def meets_version(version: str, meets: str) -> bool:
"""
Check if a version string meets a minimum version requirement.
This is a simplified version comparison tool. For robust version
comparison, consider using the 'packaging' library.
Parameters:
- version (str): Version string to check (e.g., '1.2.3')
- meets (str): Required minimum version (e.g., '1.2.0')
Returns:
- bool: True if version >= meets, False otherwise
Raises:
- ValueError: If version strings have more than 3 parts
- AssertionError: If versions are not comparable
Examples:
>>> meets_version('1.2.3', '1.2.0')
True
>>> meets_version('1.1.9', '1.2.0')
False
"""
def version_tuple(v: str) -> tuple[int, ...]:
"""
Convert a version string to a tuple of integers.
Non-numeric version components are converted to 0.
Version strings shorter than 3 parts are padded with 0.
Parameters:
- v (str): Version string (e.g., '1.2.3dev0')
Returns:
- tuple[int, ...]: Tuple of integers (e.g., (1, 2, 3))
Raises:
- ValueError: If version string has more than 3 parts
Examples:
>>> version_tuple('1.2.3')
(1, 2, 3)
>>> version_tuple('1.2.3dev0')
(1, 2, 0)
>>> version_tuple('1.2')
(1, 2, 0)
"""Detect the Python execution environment to enable environment-specific formatting.
def in_ipython() -> bool:
"""
Check if running in an IPython environment.
Returns:
- bool: True if in IPython, False otherwise
"""
def in_ipykernel() -> bool:
"""
Check if running in an ipykernel (most likely Jupyter) environment.
Note: This detects if using an ipykernel but cannot distinguish between
Jupyter Notebook, JupyterLab, QtConsole, or other frontends.
Returns:
- bool: True if using an ipykernel, False otherwise
"""Get information about Python's standard library modules for filtering purposes.
def get_standard_lib_modules() -> set[str]:
"""
Return a set of names of all modules in the standard library.
This is used internally to filter out standard library modules from
package reports and import tracking.
Returns:
- set[str]: Set of standard library module names
"""Get dependency information for installed packages using distribution metadata.
def get_distribution_dependencies(dist_name: str) -> list[str]:
"""
Get the dependencies of a specified package distribution.
Uses importlib.metadata to read distribution metadata and extract
required dependencies. This is used internally by AutoReport to
automatically include package dependencies in reports.
Parameters:
- dist_name (str): Name of the package distribution
Returns:
- list[str]: List of dependency package names
Raises:
- PackageNotFoundError: If the distribution is not found
"""import scooby
import numpy
# Get version by module name
name, version = scooby.get_version('numpy')
print(f"{name}: {version}") # numpy: 1.21.0
# Get version by module object
name, version = scooby.get_version(numpy)
print(f"{name}: {version}") # numpy: 1.21.0
# Handle missing packages
name, version = scooby.get_version('nonexistent_package')
print(f"{name}: {version}") # nonexistent_package: Module not found
# Handle packages without version info
name, version = scooby.get_version('some_local_module')
print(f"{name}: {version}") # some_local_module: Version unknown
# Check against constants
import scooby
from scooby.report import MODULE_NOT_FOUND
name, version = scooby.get_version('missing_pkg')
if version == MODULE_NOT_FOUND:
print(f"Package {name} is not installed")import scooby
# Check if installed version meets requirements
name, version = scooby.get_version('numpy')
if version and scooby.meets_version(version, '1.19.0'):
print("NumPy version is sufficient")
else:
print("NumPy version is too old or not installed")
# Version comparison examples
print(scooby.meets_version('1.2.3', '1.2.0')) # True
print(scooby.meets_version('1.1.9', '1.2.0')) # False
print(scooby.meets_version('2.0.0', '1.9.9')) # True
# Convert version strings to tuples for custom comparison
v1 = scooby.version_tuple('1.2.3dev0') # (1, 2, 0)
v2 = scooby.version_tuple('1.2.3') # (1, 2, 3)
print(v1 < v2) # Trueimport scooby
# Detect execution environment
if scooby.in_ipykernel():
print("Running in Jupyter (or similar ipykernel environment)")
# Use HTML output
report = scooby.Report()
display(report) # Will use _repr_html_()
elif scooby.in_ipython():
print("Running in IPython")
# Use enhanced IPython features
report = scooby.Report()
print(report)
else:
print("Running in standard Python")
# Use plain text output
report = scooby.Report()
print(report)import scooby
# Get all standard library modules
stdlib_modules = scooby.get_standard_lib_modules()
print(f"Standard library contains {len(stdlib_modules)} modules")
# Check if a module is in standard library
def is_third_party(module_name):
return module_name not in stdlib_modules
print(is_third_party('os')) # False (standard library)
print(is_third_party('numpy')) # True (third party)
print(is_third_party('json')) # False (standard library)from scooby.report import get_distribution_dependencies
from importlib.metadata import PackageNotFoundError
# Get dependencies for a package
try:
deps = get_distribution_dependencies('matplotlib')
print(f"matplotlib dependencies: {deps}")
# Example output: ['numpy', 'python-dateutil', 'pyparsing', ...]
except PackageNotFoundError:
print("Package not found")
# Use in combination with version checking
def analyze_package_ecosystem(package_name):
"""Analyze a package and its entire dependency tree."""
try:
# Get the main package version
name, version = scooby.get_version(package_name)
print(f"Main package: {name} {version}")
# Get its dependencies
deps = get_distribution_dependencies(package_name)
print(f"Dependencies ({len(deps)}): {', '.join(deps)}")
# Check versions of all dependencies
for dep in deps:
dep_name, dep_version = scooby.get_version(dep)
print(f" {dep_name}: {dep_version}")
except PackageNotFoundError:
print(f"Package {package_name} not found")
# Analyze an entire ecosystem
analyze_package_ecosystem('matplotlib')import scooby
def analyze_package(package_name):
"""Comprehensive analysis of a package."""
name, version = scooby.get_version(package_name)
print(f"Package: {name}")
print(f"Version: {version}")
if version and version not in ['Module not found', 'Trouble importing', 'Version unknown']:
# Check version requirements
if scooby.meets_version(version, '1.0.0'):
print("✓ Meets minimum version 1.0.0")
else:
print("✗ Below minimum version 1.0.0")
# Show version tuple
v_tuple = scooby.version_tuple(version)
print(f"Version tuple: {v_tuple}")
# Check if it's third-party
stdlib_modules = scooby.get_standard_lib_modules()
if name in stdlib_modules:
print("📦 Standard library module")
else:
print("🔧 Third-party package")
# Analyze various packages
for pkg in ['numpy', 'os', 'requests', 'nonexistent']:
analyze_package(pkg)
print()import scooby
def check_requirements(requirements):
"""
Check if installed packages meet version requirements.
Args:
requirements (dict): {package_name: min_version}
Returns:
dict: {package_name: (installed_version, meets_requirement)}
"""
import scooby
from scooby.report import MODULE_NOT_FOUND, MODULE_TROUBLE, VERSION_NOT_FOUND
results = {}
for package, min_version in requirements.items():
name, installed = scooby.get_version(package)
if installed in [MODULE_NOT_FOUND, MODULE_TROUBLE, VERSION_NOT_FOUND]:
meets_req = False
else:
meets_req = scooby.meets_version(installed, min_version)
results[name] = (installed, meets_req)
return results
# Check project requirements
requirements = {
'numpy': '1.19.0',
'pandas': '1.3.0',
'matplotlib': '3.3.0'
}
results = check_requirements(requirements)
for package, (version, meets) in results.items():
status = "✓" if meets else "✗"
print(f"{status} {package}: {version}")Install with Tessl CLI
npx tessl i tessl/pypi-scooby