Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions continuous_integration/check_dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#!/usr/bin/env python
"""
Check that dependencies are synchronized across packaging files.
Run this before releases to catch dependency mismatches.
"""

import re
import sys
from pathlib import Path


def extract_setup_py_deps(setup_path):
"""Extract install_requires from setup.py"""
content = setup_path.read_text()
match = re.search(r'install_requires=\[(.*?)\]', content, re.DOTALL)
if not match:
return set()
deps_str = match.group(1)
# Extract quoted strings
deps = re.findall(r"['\"]([^'\"]+)['\"]", deps_str)
# Remove version specifiers
deps = [d.split('>=')[0].split('==')[0].split('<')[0].strip() for d in deps]
return {d for d in deps if d and not d.startswith('#')}


def extract_requirements_txt(req_path):
"""Extract dependencies from requirements.txt"""
if not req_path.exists():
return set()
deps = []
for line in req_path.read_text().splitlines():
line = line.strip()
if line and not line.startswith('#'):
deps.append(line.split('>=')[0].split('==')[0].split('<')[0].strip())
return set(deps)


def extract_meta_yaml_deps(meta_path):
"""Extract run dependencies from meta.yaml"""
if not meta_path.exists():
return set()
content = meta_path.read_text()
# Extract run section
run_match = re.search(r'run:\s*\n(.*?)(?=\n\w+:|$)', content, re.DOTALL)
if not run_match:
return set()
run_section = run_match.group(1)
deps = []
for line in run_section.splitlines():
line = line.strip()
if line.startswith('- ') and 'python' not in line:
dep = line[2:].split('>=')[0].split('==')[0].split('<')[0].strip()
# Normalize conda package names
dep = dep.replace('matplotlib-base', 'matplotlib')
dep = dep.replace('netcdf4', 'netCDF4')
deps.append(dep)
return set(deps)


def main():
"""Check dependency synchronization"""
repo_root = Path(__file__).parent.parent

setup_py = repo_root / "setup.py"
requirements_txt = repo_root / "requirements.txt"
meta_yaml = repo_root / "recipe" / "meta.yaml"

print("Checking dependency synchronization...\n")

setup_deps = extract_setup_py_deps(setup_py)
requirements_deps = extract_requirements_txt(requirements_txt)
meta_deps = extract_meta_yaml_deps(meta_yaml)

print(f"setup.py install_requires ({len(setup_deps)} packages):")
print(f" {sorted(setup_deps)}\n")

print(f"requirements.txt ({len(requirements_deps)} packages):")
print(f" {sorted(requirements_deps)}\n")

print(f"recipe/meta.yaml run deps ({len(meta_deps)} packages):")
print(f" {sorted(meta_deps)}\n")

# Check mismatches
errors = []

# Check setup.py vs requirements.txt
missing_in_req = setup_deps - requirements_deps
if missing_in_req:
errors.append(f"⚠️ Missing in requirements.txt: {missing_in_req}")

extra_in_req = requirements_deps - setup_deps
if extra_in_req:
errors.append(f"⚠️ Extra in requirements.txt: {extra_in_req}")

# Check setup.py vs meta.yaml (excluding netCDF4 casing difference)
missing_in_meta = setup_deps - meta_deps - {'netCDF4'}
if missing_in_meta:
errors.append(f"⚠️ Missing in recipe/meta.yaml: {missing_in_meta}")

extra_in_meta = meta_deps - setup_deps
if extra_in_meta:
errors.append(f"⚠️ Extra in recipe/meta.yaml: {extra_in_meta}")

print("-" * 60)
if errors:
print("\n❌ DEPENDENCY MISMATCH DETECTED:\n")
for error in errors:
print(error)
print("\nPlease synchronize dependencies across all files before release.")
return 1
else:
print("✅ All dependencies are synchronized!")
return 0


if __name__ == "__main__":
sys.exit(main())
1 change: 1 addition & 0 deletions continuous_integration/environment-3.10.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ dependencies:
- scipy
- matplotlib
- netcdf4
- xarray
- pytest
- pytest-mpl
- pytest-cov
Expand Down
1 change: 1 addition & 0 deletions continuous_integration/environment-3.11.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ dependencies:
- scipy
- matplotlib
- netcdf4
- xarray
- pytest
- pytest-mpl
- pytest-cov
Expand Down
1 change: 1 addition & 0 deletions continuous_integration/environment-3.12.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ dependencies:
- scipy
- matplotlib
- netcdf4
- xarray
- pytest
- pytest-mpl
- pytest-cov
Expand Down
1 change: 1 addition & 0 deletions continuous_integration/environment-3.13.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ dependencies:
- scipy
- matplotlib
- netcdf4
- xarray
- pytest
- pytest-mpl
- pytest-cov
Expand Down
1 change: 1 addition & 0 deletions continuous_integration/environment_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ dependencies:
- scipy
- matplotlib
- netcdf4
- xarray
- pytest
- pytest-mpl
- pytest-cov
Expand Down
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ pint
act-atmos
nc-time-axis
openpyxl
matplotlib
scipy
dask
netCDF4
15 changes: 14 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,20 @@ def read(filename):
include_package_data=True,
packages=find_packages(exclude=('tests',)),

install_requires=['numpy', 'xarray', 'pint', 'act-atmos', 'nc-time-axis', 'openpyxl'],
install_requires=[
'numpy',
'xarray',
'pint',
'act-atmos',
'nc-time-axis',
'openpyxl',
'matplotlib',
'scipy',
'dask',
'netCDF4',
],

python_requires='>=3.10,<3.14',

classifiers=[
'Development Status :: 5 - Production/Stable',
Expand Down