11#!/usr/bin/env python3
2+ # cspell:ignore pyproject
23"""
34Script to detect strict version pins (==) in Python package dependencies.
45
1213- Changes outside of main dependency sections
1314"""
1415
16+ import json
1517import os
1618import re
1719import subprocess
1820import sys
19- from typing import Dict , List , Set , Tuple
20- import json
2121import urllib .request
2222import urllib .parse
23+ from typing import Dict , List , Optional
2324
2425
2526def run_git_command (cmd : List [str ]) -> str :
@@ -47,9 +48,8 @@ def get_changed_files(base_ref: str, head_ref: str) -> List[str]:
4748
4849 files = []
4950 for line in diff_output .strip ().split ('\n ' ):
50- if line :
51- if (line .startswith ('sdk/' ) and
52- (line .endswith ('/setup.py' ) or line .endswith ('/pyproject.toml' ))):
51+ if line and line .startswith ('sdk/' ):
52+ if line .endswith ('/setup.py' ) or line .endswith ('/pyproject.toml' ):
5353 files .append (line )
5454
5555 return files
@@ -65,12 +65,19 @@ def extract_strict_pins_from_setup_py_diff(diff_content: str) -> List[str]:
6565 bracket_depth = 0
6666
6767 for line in diff_content .split ('\n ' ):
68- # Skip the +++ file marker
68+ # Skip the +++ and --- file markers
6969 if line .startswith ('+++' ) or line .startswith ('---' ):
7070 continue
7171
7272 # Process all lines to track context, but only extract from added lines
73- actual_line = line [1 :].strip () if (line .startswith ('+' ) or line .startswith ('-' ) or line .startswith (' ' )) else line .strip ()
73+ is_added = line .startswith ('+' )
74+ is_removed = line .startswith ('-' )
75+ is_context = line .startswith (' ' )
76+
77+ if is_added or is_removed or is_context :
78+ actual_line = line [1 :].strip ()
79+ else :
80+ actual_line = line .strip ()
7481
7582 # Detect start of install_requires in any line
7683 if 'install_requires' in actual_line and '=' in actual_line :
@@ -92,18 +99,18 @@ def extract_strict_pins_from_setup_py_diff(diff_content: str) -> List[str]:
9299 # If we close all brackets, we're done with install_requires
93100 if bracket_depth <= 0 and (']' in actual_line or '),' in actual_line ):
94101 # Check current line before exiting if it's an added line
95- if line . startswith ( '+' ) and '==' in actual_line and not actual_line . strip () .startswith ('#' ):
96- match = re .search (r'["\']([^"\']+==[\d\ .]+[^"\']*)["\']' , actual_line )
102+ if is_added and '==' in actual_line and not actual_line .startswith ('#' ):
103+ match = re .search (r'["\']([^"\']+==[\d.]+[^"\']*)["\']' , actual_line )
97104 if match :
98105 strict_pins .append (match .group (1 ))
99106 in_install_requires = False
100107 continue
101108
102109 # Look for strict pins in added lines within install_requires
103- if in_install_requires and line . startswith ( '+' ) :
104- if '==' in actual_line and not actual_line .strip (). startswith ('#' ):
110+ if in_install_requires and is_added :
111+ if '==' in actual_line and not actual_line .startswith ('#' ):
105112 # Match package==version pattern
106- match = re .search (r'["\']([^"\']+==[\d\ .]+[^"\']*)["\']' , actual_line )
113+ match = re .search (r'["\']([^"\']+==[\d.]+[^"\']*)["\']' , actual_line )
107114 if match :
108115 strict_pins .append (match .group (1 ))
109116
@@ -125,13 +132,20 @@ def extract_strict_pins_from_pyproject_diff(diff_content: str) -> List[str]:
125132 continue
126133
127134 # Process all lines to track context
128- actual_line = line [1 :].strip () if (line .startswith ('+' ) or line .startswith ('-' ) or line .startswith (' ' )) else line .strip ()
135+ is_added = line .startswith ('+' )
136+ is_removed = line .startswith ('-' )
137+ is_context = line .startswith (' ' )
138+
139+ if is_added or is_removed or is_context :
140+ actual_line = line [1 :].strip ()
141+ else :
142+ actual_line = line .strip ()
129143
130144 # Detect [project] section markers in any line (context or changes)
131145 if actual_line .startswith ('[' ):
132146 if actual_line .startswith ('[project]' ):
133147 in_other_section = False
134- elif actual_line . startswith ( '[' ) :
148+ else :
135149 in_other_section = True
136150 in_project_dependencies = False
137151
@@ -148,10 +162,10 @@ def extract_strict_pins_from_pyproject_diff(diff_content: str) -> List[str]:
148162 continue
149163
150164 # Look for strict pins in added lines within [project] dependencies
151- if in_project_dependencies and line . startswith ( '+' ) :
152- if '==' in actual_line and not actual_line .strip (). startswith ('#' ):
165+ if in_project_dependencies and is_added :
166+ if '==' in actual_line and not actual_line .startswith ('#' ):
153167 # Match package==version pattern
154- match = re .search (r'["\']([^"\']+==[\d\ .]+[^"\']*)["\']' , actual_line )
168+ match = re .search (r'["\']([^"\']+==[\d.]+[^"\']*)["\']' , actual_line )
155169 if match :
156170 strict_pins .append (match .group (1 ))
157171
@@ -213,7 +227,7 @@ def check_architect_approval(pr_number: str, repo: str, github_token: str) -> bo
213227 return False
214228
215229
216- def set_output (name : str , value : str ):
230+ def set_output (name : str , value : str ) -> None :
217231 """Set GitHub Actions output."""
218232 github_output = os .getenv ('GITHUB_OUTPUT' )
219233 if github_output :
@@ -225,14 +239,15 @@ def set_output(name: str, value: str):
225239 print (f"::set-output name={ name } ::{ value } " )
226240
227241
228- def main ():
242+ def main () -> int :
243+ """Main function to check for strict version pins."""
229244 base_ref = os .getenv ('BASE_REF' , 'origin/main' )
230245 head_ref = os .getenv ('HEAD_REF' , 'HEAD' )
231246 pr_number = os .getenv ('PR_NUMBER' )
232247 repo = os .getenv ('REPO' )
233248 github_token = os .getenv ('GITHUB_TOKEN' )
234249
235- print (f "Checking for strict version pins..." )
250+ print ("Checking for strict version pins..." )
236251 print (f"Base: { base_ref } , Head: { head_ref } " )
237252
238253 # Get changed files
@@ -246,11 +261,11 @@ def main():
246261 return 0
247262
248263 print (f"Checking { len (changed_files )} file(s):" )
249- for f in changed_files :
250- print (f" - { f } " )
264+ for file_path in changed_files :
265+ print (f" - { file_path } " )
251266
252267 # Check each file for strict pins
253- all_strict_pins = {}
268+ all_strict_pins : Dict [ str , List [ str ]] = {}
254269 for filepath in changed_files :
255270 strict_pins = check_file_for_strict_pins (filepath , base_ref , head_ref )
256271 if strict_pins :
0 commit comments