Skip to content

Commit eaf9002

Browse files
committed
- Broadly enhanced build infrastructure, allowing dynamic versioning both at installation time and via CLI post installation.
- **New Feature**: - Initially, the dynamic versioning system allows toggling between Lightning unified and standalone imports. - The two conversion operations are individually idempotent and mutually reversible. - utils.py - Core import conversion functionality - toggle_lightning_mode.py - CLI tool - **Package Configuration**: Modernized pyproject.toml to use PEP 639 metadata - **Dependencies**: Replaced hardcoded Lightning dependencies with dynamic detection system - **Version Handling**: Added support for dynamic requirements via commit pins - **Entry Points**: Added CLI command registration for `toggle-lightning-mode` utility - **Environment Variables**: Added `USE_CI_COMMIT_PINS="1"` to GitHub workflows and Azure pipelines - **Docker**: Updated base-cuda Dockerfile to use commit pins and upgrade setuptools - **New File**: Added lightning_pin.txt to track specific Lightning commits - **Removed**: Deleted standalone base requirements file in favor of dynamic generation - **Build**: Enhanced setup.py to handle different Lightning import styles - **Removed**: Cleaned up deprecated code from assistant.py - **Packaging**: Updated MANIFEST.in to exclude tests directory and include toggle script
1 parent 196d515 commit eaf9002

File tree

22 files changed

+1081
-253
lines changed

22 files changed

+1081
-253
lines changed

.actions/assistant.py

Lines changed: 1 addition & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -2,124 +2,16 @@
22
import datetime
33
import os
44
import re
5-
import shutil
6-
from collections.abc import ValuesView
7-
from os.path import dirname, isfile
85
from pprint import pprint
9-
from typing import List, Optional, Sequence, Tuple
6+
from typing import Sequence
107

118
REQUIREMENT_FILES = [
129
"requirements/base.txt",
1310
"requirements/extra.txt",
1411
"requirements/examples.txt",
1512
]
1613

17-
18-
def _retrieve_files(directory: str, *ext: str) -> List[str]:
19-
all_files = []
20-
for root, _, files in os.walk(directory):
21-
for fname in files:
22-
if not ext or any(os.path.split(fname)[1].lower().endswith(e) for e in ext):
23-
all_files.append(os.path.join(root, fname))
24-
25-
return all_files
26-
27-
28-
def _replace_standalone_imports(lines: List[str], mapping: List[Tuple[str, str]], lightning_by: str = "") -> List[str]:
29-
"""Replace imports of standalone package to lightning.
30-
31-
Adapted from `assistant._replace_imports`
32-
"""
33-
out = lines[:]
34-
for source_import, target_import in mapping:
35-
for i, ln in enumerate(out):
36-
out[i] = re.sub(
37-
rf"([^_/@]|^){source_import}([^_\w/]|$)",
38-
rf"\1{target_import}\2",
39-
ln,
40-
)
41-
if lightning_by: # in addition, replace base package
42-
out[i] = out[i].replace("from lightning import ", f"from {lightning_by} import ")
43-
out[i] = out[i].replace("import lightning ", f"import {lightning_by} ")
44-
return out
45-
46-
47-
def _replace_unified_imports(lines: List[str], mapping: List[Tuple[str, str]], lightning_by: str = "") -> List[str]:
48-
"""Replace imports of standalone package to unified lightning.
49-
50-
Adapted from `assistant._replace_imports`
51-
"""
52-
out = lines[:]
53-
for source_import, target_import in mapping:
54-
for i, ln in enumerate(out):
55-
out[i] = re.sub(
56-
rf"([^_/@]|^){source_import}([^_\w/]|$)",
57-
rf"\1{target_import}\2",
58-
ln,
59-
)
60-
if lightning_by: # in addition, replace base package
61-
out[i] = out[i].replace("from lightning import ", f"from {lightning_by} import ")
62-
out[i] = out[i].replace("import lightning ", f"import {lightning_by} ")
63-
return out
64-
65-
66-
def copy_replace_imports(
67-
source_dir: str,
68-
source_imports: Sequence[str],
69-
target_imports: Sequence[str],
70-
target_dir: Optional[str] = None,
71-
lightning_by: str = "",
72-
) -> None:
73-
"""Replace package content with import adjustments.
74-
75-
Adapted from `assistant.copy_replace_imports`
76-
"""
77-
print(f"Replacing imports: {locals()}")
78-
assert len(source_imports) == len(target_imports), (
79-
"source and target imports must have the same length, "
80-
f"source: {len(source_imports)}, target: {len(target_imports)}"
81-
)
82-
if target_dir is None:
83-
target_dir = source_dir
84-
85-
ls = _retrieve_files(source_dir)
86-
for fp in ls:
87-
fp_new = fp.replace(source_dir, target_dir)
88-
_, ext = os.path.splitext(fp)
89-
if ext in (".png", ".jpg", ".ico"):
90-
os.makedirs(dirname(fp_new), exist_ok=True)
91-
if not isfile(fp_new):
92-
shutil.copy(fp, fp_new)
93-
continue
94-
elif ext in (".pyc",):
95-
continue
96-
# Try to parse everything else
97-
with open(fp, encoding="utf-8") as fo:
98-
try:
99-
lines = fo.readlines()
100-
except UnicodeDecodeError:
101-
# a binary file, skip
102-
print(f"Skipped replacing imports for {fp}")
103-
continue
104-
lines = _replace_standalone_imports(lines, list(zip(source_imports, target_imports)), lightning_by=lightning_by)
105-
os.makedirs(os.path.dirname(fp_new), exist_ok=True)
106-
with open(fp_new, "w", encoding="utf-8") as fo:
107-
fo.writelines(lines)
108-
109-
110-
def use_standalone_pl(mapping, src_dirs: ValuesView) -> None:
111-
for in_place_path in src_dirs:
112-
for _, _ in mapping.items():
113-
copy_replace_imports(
114-
source_dir=str(in_place_path),
115-
source_imports=mapping.keys(),
116-
target_imports=mapping.values(),
117-
target_dir=str(in_place_path),
118-
)
119-
120-
12114
class AssistantCLI:
122-
12315
_PATH_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
12416

12517
@staticmethod
@@ -171,24 +63,6 @@ def prepare_nightly_version(proj_root: str = _PATH_ROOT) -> None:
17163
with open(path_info, "w") as fp:
17264
fp.write(init)
17365

174-
@staticmethod
175-
def copy_replace_imports(
176-
source_dir: str,
177-
source_import: str,
178-
target_import: str,
179-
target_dir: Optional[str] = None,
180-
lightning_by: str = "",
181-
) -> None:
182-
"""Copy package content in-place with import adjustments.
183-
184-
Adapated from Lightning version of `assistant`.
185-
"""
186-
source_imports = source_import.strip().split(",")
187-
target_imports = target_import.strip().split(",")
188-
copy_replace_imports(
189-
source_dir, source_imports, target_imports, target_dir=target_dir, lightning_by=lightning_by
190-
)
191-
19266

19367
if __name__ == "__main__":
19468
import jsonargparse

.azure-pipelines/gpu-tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ jobs:
7272
pip install --upgrade pip requests setuptools
7373
pip install -e . --no-warn-script-location --requirement requirements/devel.txt
7474
pip install lightning --upgrade
75-
#env:
76-
#PACKAGE_NAME: "pytorch"
75+
env:
76+
USE_CI_COMMIT_PINS: "1"
7777
displayName: 'Install dependencies'
7878
7979
- bash: |

.github/workflows/ci_test-full.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ jobs:
7171
run: python -c "import time; days = time.time() / 60 / 60 / 24; print(f'TIME_PERIOD=d{int(days / 2) * 2}')" >> $GITHUB_ENV
7272

7373
- name: basic setup
74+
env:
75+
USE_CI_COMMIT_PINS: "1"
7476
run: |
7577
pip --version
7678
pip install --requirement requirements/cli.txt --upgrade

.github/workflows/code-checks.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ jobs:
1717
- uses: actions/setup-python@v5
1818
with:
1919
python-version: '3.12'
20+
env:
21+
USE_CI_COMMIT_PINS: "1"
2022
- name: Install dependencies
2123
run: |
2224
pip install '.[dev]'

.github/workflows/release-pypi.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232

3333
- name: Install dependencies
3434
run: >-
35-
python -m pip install --user --upgrade build
35+
python -m pip install --user --upgrade build twine setuptools
3636
3737
- name: Build packages
3838
run: |

MANIFEST.in

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ recursive-exclude __pycache__ *.py[cod] *.orig lightning_logs
44
prune src/fts_examples/ipynb_src
55
# exclude fts_examples tests
66
exclude src/fts_examples/test_examples.py
7+
# Exclude tests directory
8+
prune tests
79
# Include the README and CHANGELOG
810
include *.md
911
# Include the license file
@@ -13,6 +15,8 @@ include .actions/assistant.py
1315
include *.cff
1416
# Include marker file for PEP 561
1517
include src/finetuning_scheduler/py.typed
18+
# Explicitly include the toggle script to ensure entry points work
19+
include src/finetuning_scheduler/dynamic_versioning/toggle_lightning_mode.py
1620
# Include the Requirements
1721
recursive-include requirements *.txt
1822
include requirements.txt

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,26 @@ wget https://github.com/speediedan/finetuning-scheduler/releases/download/v${FTS
122122
pip install finetuning-scheduler-${FTS_VERSION}.tar.gz
123123
```
124124

125+
### Toggling Between Unified and Standalone Lightning Imports
126+
127+
FTS provides a command-line tool to easily toggle between unified and standalone imports in your codebase:
128+
129+
```bash
130+
# Toggle from unified to standalone Lightning imports
131+
toggle-lightning-mode --mode standalone
132+
133+
# Toggle from standalone to unified Lightning imports (default)
134+
toggle-lightning-mode --mode unified
135+
# Or simply
136+
toggle-lightning-mode
137+
```
138+
139+
This can be useful when:
140+
141+
- You need to adapt existing code to work with a different Lightning package
142+
- You're switching between projects using different Lightning import styles
143+
- You want to test compatibility with both import styles
144+
125145
______________________________________________________________________
126146

127147
## Examples

dockers/base-cuda/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,13 @@ COPY ./requirements.txt requirements.txt
6464
COPY ./requirements/ ./requirements/
6565

6666
ENV PYTHONPATH=/usr/lib/python${PYTHON_VERSION}/site-packages
67+
ENV USE_CI_COMMIT_PINS="1"
6768

6869
RUN \
6970
wget https://bootstrap.pypa.io/get-pip.py --progress=bar:force:noscroll --no-check-certificate | python${PYTHON_VERSION} && \
7071
python${PYTHON_VERSION} get-pip.py && \
7172
rm get-pip.py && \
72-
python${PYTHON_VERSION} -m pip install --upgrade pip && \
73+
python${PYTHON_VERSION} -m pip install --upgrade pip setuptools && \
7374
# Disable cache
7475
pip config set global.cache-dir false && \
7576
pip install virtualenv && \

pyproject.toml

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,70 @@
11
[build-system]
2+
# we require a setuptools version with PEP 639 support
23
requires = [
3-
"setuptools",
4+
"setuptools>=77.0.0",
45
"wheel",
56
]
7+
build-backend = "setuptools.build_meta"
8+
9+
[project]
10+
name = "finetuning-scheduler"
11+
dynamic = ["version", "description", "readme", "dependencies", "optional-dependencies"]
12+
authors = [{name = "Daniel Dale", email = "[email protected]"}]
13+
license = "Apache-2.0"
14+
license-files = ["LICENSE*"]
15+
requires-python = ">=3.9"
16+
keywords = [
17+
"deep learning",
18+
"pytorch",
19+
"AI",
20+
"machine learning",
21+
"pytorch-lightning",
22+
"lightning",
23+
"fine-tuning",
24+
"finetuning",
25+
]
26+
classifiers = [
27+
"Environment :: Console",
28+
"Natural Language :: English",
29+
"Development Status :: 5 - Production/Stable",
30+
"Intended Audience :: Developers",
31+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
32+
"Topic :: Scientific/Engineering :: Image Recognition",
33+
"Topic :: Scientific/Engineering :: Information Analysis",
34+
"Operating System :: OS Independent",
35+
"Programming Language :: Python :: 3",
36+
"Programming Language :: Python :: 3.9",
37+
"Programming Language :: Python :: 3.10",
38+
"Programming Language :: Python :: 3.11",
39+
"Programming Language :: Python :: 3.12",
40+
]
41+
42+
[project.urls]
43+
"Homepage" = "https://github.com/speediedan/finetuning-scheduler"
44+
"Bug Tracker" = "https://github.com/speediedan/finetuning-scheduler/issues"
45+
"Documentation" = "https://finetuning-scheduler.readthedocs.io/en/stable/"
46+
"Source Code" = "https://github.com/speediedan/finetuning-scheduler"
47+
48+
[project.scripts]
49+
toggle-lightning-mode = "finetuning_scheduler.dynamic_versioning.toggle_lightning_mode:main"
50+
51+
[tool.setuptools]
52+
package-dir = {"" = "src"}
53+
include-package-data = true
54+
zip-safe = false
55+
56+
[tool.setuptools.packages.find]
57+
where = ["src"]
58+
namespaces = true
59+
60+
[tool.setuptools.package-data]
61+
"fts_examples.config" = ["*.yaml"]
62+
"fts_examples.config.advanced.fsdp" = ["*.yaml"]
63+
"fts_examples.config.advanced.reinit_lr" = ["*.yaml"]
64+
"fts_examples.config.advanced.reinit_optim_lr" = ["*.yaml"]
65+
"fts_examples.model_parallel.config" = ["*.yaml"]
66+
"fts_examples.model_parallel.config.defaults" = ["*.yaml"]
67+
"fts_examples.model_parallel.config.profiling" = ["*.yaml"]
668

769
[tool.ruff]
870
line-length = 120
@@ -23,7 +85,7 @@ exclude = [
2385
"build",
2486
"temp",
2587
]
26-
lint.ignore-init-module-imports = true
88+
# lint.ignore-init-module-imports = true
2789
output-format = "pylint"
2890

2991
[tool.ruff.per-file-ignores]

requirements/base.txt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
1-
#lightning>=2.6.0,<2.6.1
2-
# the below is uncommented when master is targeting a specific pl dev master commit
3-
git+https://github.com/Lightning-AI/lightning.git@ca13f77eab5d8b85ee84eb9fd7484c324ba198b1#egg=lightning
1+
# lightning or pytorch-lightning ">=2.6.0,<2.6.1"
42
torch>=2.3.0

0 commit comments

Comments
 (0)