Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Aug 2, 2025

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:

  1. Uses only the CI workflow's LATEST_PYTHON_VERSION constant as the single source of truth
  2. Maintains hardcoded Python classifiers in setup.py as placeholders
  3. Dynamically updates setup.py during CI runs using plaintext replacement

Dynamic setup.py Updates: The CI workflow now includes a step that:

  1. Extracts the minor version from LATEST_PYTHON_VERSION (e.g., "3.13" → "13")
  2. Calculates the 5 supported versions (latest + 4 previous)
  3. Uses a Python script to replace version numbers in setup.py's classifiers before running tests

Simplified Maintenance: When Python 3.14 is released, maintainers only need to change one line:

env:
  LATEST_PYTHON_VERSION: "3.14"  # Only this line changes in ci.yml

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:

  • Generates Python version matrix for testing
  • Includes new step to update setup.py classifiers before running tests
  • Uses Python script for reliable string replacement

setup.py:

  • Contains static Python classifiers that serve as replacement targets
  • No longer includes version generation logic or constants
  • Automatically updated during CI runs to match the test matrix

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.

…+ 4 previous versions

Co-authored-by: xgboosted <1528712+xgboosted@users.noreply.github.com>
Copilot AI changed the title [WIP] Support latest stable Python version and previous 4 versions in CI, parameterized from a single constant Parameterize Python versions in CI to support latest stable + 4 previous versions Aug 2, 2025
Copilot AI requested a review from xgboosted August 2, 2025 11:59
Copilot finished work on behalf of xgboosted August 2, 2025 11:59
@xgboosted
Copy link
Owner

@copilot

The older Python versions need to be derived from the latest Python version, which is a constant.

python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']

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>
Copy link
Contributor Author

Copilot AI commented Aug 2, 2025

@copilot

The older Python versions need to be derived from the latest Python version, which is a constant.
...

I've updated the CI workflow to derive Python versions dynamically from the LATEST_PYTHON_VERSION constant. The implementation:

  1. Extracts the minor version from "3.13" → "13"
  2. Generates 5 versions by subtracting 4, 3, 2, 1, 0 from the minor version
  3. Creates a JSON array ["3.9", "3.10", "3.11", "3.12", "3.13"]
  4. Uses fromJSON() to parse this into the test matrix

Now when Python 3.14 is released, only the LATEST_PYTHON_VERSION needs to be updated and the matrix will automatically become ["3.10", "3.11", "3.12", "3.13", "3.14"].

Commit: e2b32c0

Copilot AI changed the title Parameterize Python versions in CI to support latest stable + 4 previous versions Parameterize Python versions in CI with dynamic matrix generation from latest version constant Aug 2, 2025
Copilot finished work on behalf of xgboosted August 2, 2025 12:32
@xgboosted
Copy link
Owner

@copilot

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>
Copy link
Contributor Author

Copilot AI commented Aug 2, 2025

@copilot

The supported Python version in setup.py needs to be updated right after ci.yml finishes forming the 5 Python versions.

I've updated setup.py to dynamically generate Python version classifiers from the same LATEST_PYTHON_VERSION constant used in the CI workflow. The implementation:

  1. Adds LATEST_PYTHON_VERSION = "3.13" constant to setup.py
  2. Creates generate_python_classifiers() function that extracts the minor version and generates classifiers for latest + 4 previous versions
  3. Replaces hardcoded classifiers with dynamic generation using the same logic as CI

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 AI changed the title Parameterize Python versions in CI with dynamic matrix generation from latest version constant Parameterize Python versions in CI and setup.py with dynamic matrix generation from latest version constant Aug 2, 2025
Copilot finished work on behalf of xgboosted August 2, 2025 12:41
@xgboosted
Copy link
Owner

@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>
Copy link
Contributor Author

Copilot AI commented Aug 2, 2025

@copilot couldn't the setup.py file be updated by plaintext replacements code in ci.yml instead of having a dedicated code block?

Yes, excellent suggestion! I've refactored the implementation to centralize all version logic in the CI workflow using plaintext replacement. Now:

  1. setup.py contains hardcoded Python classifiers that serve as placeholders
  2. CI workflow dynamically generates versions and replaces the classifiers in setup.py during testing
  3. Single source of truth: Only ci.yml contains the LATEST_PYTHON_VERSION constant

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 ci.yml needs to be updated and setup.py will automatically be updated during CI runs.

Commit: 5b4a01e

…ted backup

Co-authored-by: xgboosted <1528712+xgboosted@users.noreply.github.com>
Copilot AI changed the title Parameterize Python versions in CI and setup.py with dynamic matrix generation from latest version constant Centralize Python version management in CI workflow with dynamic setup.py updates Aug 2, 2025
Copilot finished work on behalf of xgboosted August 2, 2025 12:54
@xgboosted xgboosted marked this pull request as ready for review August 2, 2025 13:25
Copilot AI review requested due to automatic review settings August 2, 2025 13:25
@xgboosted xgboosted merged commit 512893e into main Aug 2, 2025
15 checks passed
@xgboosted xgboosted deleted the copilot/fix-11 branch August 2, 2025 13:25
Copy link
Contributor

Copilot AI left a 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_VERSION environment 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
Copy link

Copilot AI Aug 2, 2025

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.

Copilot uses AI. Check for mistakes.
Comment on lines +86 to +109
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:
Copy link

Copilot AI Aug 2, 2025

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.

Suggested change
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:

Copilot uses AI. Check for mistakes.
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)
Copy link

Copilot AI Aug 2, 2025

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.

Suggested change
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)

Copilot uses AI. Check for mistakes.
classifiers=[
"Development Status :: 4 - Beta",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.9",
Copy link

Copilot AI Aug 2, 2025

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.

Suggested change
"Programming Language :: Python :: 3.9",

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support latest stable Python version and previous 4 versions in CI, parameterized from a single constant

2 participants