-
-
Notifications
You must be signed in to change notification settings - Fork 46
Centralize Python version management in CI workflow with dynamic setup.py updates #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…+ 4 previous versions Co-authored-by: xgboosted <1528712+xgboosted@users.noreply.github.com>
|
The older Python versions need to be derived from the latest Python version, which is a constant.
The above line of code in ci.yml directly defines the older versions instead of deriving the last 4 versions. Address this by extracting the string after the decimal point and converting to an integer, then negating 4 times to obtain the last 4 versions. |
- Added generate-matrix job to calculate Python versions programmatically - Extracts minor version from LATEST_PYTHON_VERSION and generates 5 versions (latest + 4 previous) - Test job now depends on generate-matrix and uses fromJSON() to parse the dynamic matrix - Maintains the same 5-version support window while making it fully parameterized Co-authored-by: xgboosted <1528712+xgboosted@users.noreply.github.com>
I've updated the CI workflow to derive Python versions dynamically from the
Now when Python 3.14 is released, only the Commit: e2b32c0 |
|
The supported Python version in setup.py needs to be updated right after ci.yml finishes forming the 5 Python versions. |
…eneration Co-authored-by: xgboosted <1528712+xgboosted@users.noreply.github.com>
I've updated setup.py to dynamically generate Python version classifiers from the same
Now both CI and setup.py use the same approach - when Python 3.14 is released, only the constant needs to be updated in both files and all Python version references will automatically update. Commit: 83f7132 |
|
@copilot couldn't the setup.py file be updated by plaintext replacements code in ci.yml instead of having a dedicated code block? |
…instead of dedicated code Co-authored-by: xgboosted <1528712+xgboosted@users.noreply.github.com>
Yes, excellent suggestion! I've refactored the implementation to centralize all version logic in the CI workflow using plaintext replacement. Now:
The CI now includes a step that uses a Python script to find and replace Python version classifiers in setup.py before running tests. This eliminates the duplicate code and constants while ensuring consistency between CI matrix and package metadata. When Python 3.14 is released, only the constant in Commit: 5b4a01e |
…ted backup Co-authored-by: xgboosted <1528712+xgboosted@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR centralizes Python version management by using a single environment variable in the CI workflow as the source of truth, eliminating duplicate version logic between CI and setup.py files. The approach dynamically generates a test matrix and updates setup.py classifiers during CI runs.
Key changes:
- Introduced
LATEST_PYTHON_VERSIONenvironment variable as single source of truth - Added dynamic matrix generation job that calculates 5 consecutive Python versions
- Implemented runtime setup.py modification to sync classifiers with test matrix
Reviewed Changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| .github/workflows/ci.yml | Added environment variable, matrix generation job, and setup.py update step |
| setup.py | Updated Python classifiers to include 3.9 and 3.13 as static placeholders |
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Update setup.py with dynamic Python classifiers |
Copilot
AI
Aug 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The inline Python script is complex and difficult to maintain within the YAML workflow. Consider extracting this logic to a separate Python script file (e.g., scripts/update_classifiers.py) and calling it from the workflow step.
| content = f.read() | ||
| # Find and replace each Python classifier line | ||
| lines = content.split('\n') | ||
| version_index = 0 | ||
| for i, line in enumerate(lines): | ||
| if '"Programming Language :: Python :: 3.' in line and version_index < len(target_versions): | ||
| # Replace the version number in this line | ||
| import re | ||
| new_line = re.sub(r'3\.\d+', target_versions[version_index], line) | ||
| lines[i] = new_line | ||
| version_index += 1 | ||
| # Write back to setup.py | ||
| with open('setup.py', 'w') as f: | ||
| f.write('\n'.join(lines)) | ||
| print(f"Updated setup.py with Python versions: {', '.join(target_versions)}") | ||
| # Verify the changes | ||
| with open('setup.py', 'r') as f: | ||
| for line_num, line in enumerate(f, 1): | ||
| if '"Programming Language :: Python :: 3.' in line: |
Copilot
AI
Aug 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic assumes Python classifier lines appear in order and will only update the first 5 matching lines. If setup.py contains non-consecutive Python classifiers or additional Python version lines, this could lead to incorrect replacements or missed updates.
| content = f.read() | |
| # Find and replace each Python classifier line | |
| lines = content.split('\n') | |
| version_index = 0 | |
| for i, line in enumerate(lines): | |
| if '"Programming Language :: Python :: 3.' in line and version_index < len(target_versions): | |
| # Replace the version number in this line | |
| import re | |
| new_line = re.sub(r'3\.\d+', target_versions[version_index], line) | |
| lines[i] = new_line | |
| version_index += 1 | |
| # Write back to setup.py | |
| with open('setup.py', 'w') as f: | |
| f.write('\n'.join(lines)) | |
| print(f"Updated setup.py with Python versions: {', '.join(target_versions)}") | |
| # Verify the changes | |
| with open('setup.py', 'r') as f: | |
| for line_num, line in enumerate(f, 1): | |
| if '"Programming Language :: Python :: 3.' in line: | |
| lines = f.readlines() | |
| # Find the classifiers list and its indentation | |
| classifiers_start = None | |
| classifiers_end = None | |
| for i, line in enumerate(lines): | |
| if re.match(r'\s*classifiers\s*=\s*\[', line): | |
| classifiers_start = i | |
| break | |
| if classifiers_start is not None: | |
| # Find the end of the classifiers list | |
| for j in range(classifiers_start + 1, len(lines)): | |
| if re.match(r'\s*\]', lines[j]): | |
| classifiers_end = j | |
| break | |
| if classifiers_start is not None and classifiers_end is not None: | |
| # Remove all Python 3.x classifier lines | |
| new_classifiers = [] | |
| for line in lines[classifiers_start+1:classifiers_end]: | |
| if not re.search(r'Programming Language :: Python :: 3\.\d+', line): | |
| new_classifiers.append(line) | |
| # Determine indentation | |
| indent_match = re.match(r'^(\s*)', lines[classifiers_start+1]) | |
| indent = indent_match.group(1) if indent_match else " " | |
| # Add the new classifier lines | |
| for v in target_versions: | |
| new_classifiers.append(f'{indent}"Programming Language :: Python :: {v}",\n') | |
| # Replace the lines in the file | |
| lines = ( | |
| lines[:classifiers_start+1] + | |
| new_classifiers + | |
| lines[classifiers_end:] | |
| ) | |
| with open('setup.py', 'w') as f: | |
| f.writelines(lines) | |
| print(f"Updated setup.py with Python versions: {', '.join(target_versions)}") | |
| else: | |
| print("Could not find classifiers list in setup.py") | |
| # Verify the changes | |
| with open('setup.py', 'r') as f: | |
| for line_num, line in enumerate(f, 1): | |
| if 'Programming Language :: Python :: 3.' in line: |
| if '"Programming Language :: Python :: 3.' in line and version_index < len(target_versions): | ||
| # Replace the version number in this line | ||
| import re | ||
| new_line = re.sub(r'3\.\d+', target_versions[version_index], line) |
Copilot
AI
Aug 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The regex pattern 3\.\d+ will match any sequence like '3.X' in the line, potentially replacing unintended content. Use a more specific pattern like r'"Programming Language :: Python :: 3\.\d+' to ensure only the classifier version is replaced.
| new_line = re.sub(r'3\.\d+', target_versions[version_index], line) | |
| new_line = re.sub(r'("Programming Language :: Python :: )3\.\d+', r'\1' + target_versions[version_index], line) |
| classifiers=[ | ||
| "Development Status :: 4 - Beta", | ||
| "Programming Language :: Python :: 3.10", | ||
| "Programming Language :: Python :: 3.9", |
Copilot
AI
Aug 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding Python 3.9 support contradicts the PR description which states supporting 'latest + 4 previous versions'. If LATEST_PYTHON_VERSION is 3.13, the supported versions should be 3.9-3.13, but the description implies 3.10-3.14 range.
| "Programming Language :: Python :: 3.9", |
This PR refactors the Python version management approach to use a single source of truth in the CI workflow, eliminating code duplication between CI and setup.py files.
Key Changes
Centralized Version Logic: Instead of maintaining duplicate version generation logic in both files, the solution now:
LATEST_PYTHON_VERSIONconstant as the single source of truthDynamic setup.py Updates: The CI workflow now includes a step that:
LATEST_PYTHON_VERSION(e.g., "3.13" → "13")Simplified Maintenance: When Python 3.14 is released, maintainers only need to change one line:
The CI matrix automatically becomes
["3.10", "3.11", "3.12", "3.13", "3.14"]and setup.py classifiers are updated during testing, without any manual setup.py modifications.Implementation Details
CI Workflow:
setup.py:
This approach eliminates code duplication while ensuring consistency between CI testing matrix and package metadata, making Python version updates truly require only a single line change.
Fixes #11.
💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.