Skip to content

Commit dfdbd04

Browse files
committed
Add upper limit on python version for uv and pixi
1 parent f39065a commit dfdbd04

File tree

1 file changed

+108
-37
lines changed

1 file changed

+108
-37
lines changed

hooks/post_gen_project.py

Lines changed: 108 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,48 @@ def read_toml(filepath: str):
2727
return tomli.load(f)
2828

2929

30+
def bump_semver(version: str, part: str = "minor") -> str:
31+
"""
32+
Bump a semantic version string.
33+
34+
Args:
35+
version: A semantic version string (e.g., '3.8.0')
36+
part: Which part to bump ('major', 'minor', or 'patch')
37+
38+
Returns:
39+
The bumped version string
40+
"""
41+
parts = version.split(".")
42+
major, minor, patch = (
43+
int(parts[0]),
44+
int(parts[1]) if len(parts) > 1 else 0,
45+
int(parts[2]) if len(parts) > 2 else 0,
46+
)
47+
48+
if part == "major":
49+
major += 1
50+
minor = 0
51+
patch = 0
52+
elif part == "minor":
53+
minor += 1
54+
patch = 0
55+
elif part == "patch":
56+
patch += 1
57+
58+
return f"{major}.{minor}.{patch}"
59+
60+
3061
def update_pyproject_toml():
3162
"""Update pyproject.toml with project config and dependencies."""
3263
project_config = read_toml("project_config.toml")
3364
dependencies = read_toml("dependencies.toml")
34-
65+
3566
# Read the current pyproject.toml
3667
with open("pyproject.toml", "rb") as f:
3768
pyproject = tomli.load(f)
38-
69+
3970
package_manager = "{{cookiecutter.package_manager}}".lower()
40-
71+
4172
# Add project configuration
4273
if package_manager == "poetry":
4374
# Poetry uses tool.poetry for project metadata
@@ -54,109 +85,147 @@ def update_pyproject_toml():
5485
"packages": [project_config["project"]["package_dir"]],
5586
"exclude": project_config["project"]["exclude"],
5687
}
57-
88+
5889
# Add dependencies
59-
pyproject["tool"]["poetry"]["dependencies"] = {"python": f"^{project_config['project']['python_version']}"}
90+
pyproject["tool"]["poetry"]["dependencies"] = {
91+
"python": f"^{project_config['project']['python_version']}"
92+
}
6093
for name, version in dependencies["dependencies"].items():
6194
pyproject["tool"]["poetry"]["dependencies"][name] = f"^{version}"
62-
95+
6396
# Add dev dependencies
6497
pyproject["tool"]["poetry"].setdefault("group", {})
65-
pyproject["tool"]["poetry"]["group"]["dev"] = {"optional": True, "dependencies": {}}
98+
pyproject["tool"]["poetry"]["group"]["dev"] = {
99+
"optional": True,
100+
"dependencies": {},
101+
}
66102
for name, version in dependencies["dev-dependencies"].items():
67103
if isinstance(version, dict):
68104
# Handle complex dependencies like black = {version = "23.7.0", extras = ["jupyter"]}
69105
pyproject["tool"]["poetry"]["group"]["dev"]["dependencies"][name] = {
70106
"version": f"^{version['version']}",
71-
"extras": version.get("extras", [])
107+
"extras": version.get("extras", []),
72108
}
73109
else:
74-
pyproject["tool"]["poetry"]["group"]["dev"]["dependencies"][name] = "*" if version == "*" else f"^{version}"
75-
110+
pyproject["tool"]["poetry"]["group"]["dev"]["dependencies"][name] = (
111+
"*" if version == "*" else f"^{version}"
112+
)
113+
76114
# Add poetry-specific tool tasks
77-
pyproject["tool"]["poe"]["tasks"]["_poetry_install_sort_plugin"] = "poetry self add poetry-plugin-sort"
115+
pyproject["tool"]["poe"]["tasks"][
116+
"_poetry_install_sort_plugin"
117+
] = "poetry self add poetry-plugin-sort"
78118
pyproject["tool"]["poe"]["tasks"]["_poetry_sort"] = "poetry sort"
79-
pyproject["tool"]["poe"]["tasks"]["format"] = ["_ruff_format", "_ruff_format_nb", "_black_format", "_poetry_install_sort_plugin", "_poetry_sort"]
80-
119+
pyproject["tool"]["poe"]["tasks"]["format"] = [
120+
"_ruff_format",
121+
"_ruff_format_nb",
122+
"_black_format",
123+
"_poetry_install_sort_plugin",
124+
"_poetry_sort",
125+
]
126+
81127
elif package_manager == "uv":
82128
# UV uses standard project metadata
129+
83130
pyproject["project"] = {
84131
"name": project_config["project"]["name"],
85132
"version": project_config["project"]["version"],
86133
"description": project_config["project"]["description"],
87-
"authors": [{"name": author.split("<")[0].strip(), "email": author.split("<")[1].strip(">")}
88-
for author in project_config["project"]["authors"]],
134+
"authors": [
135+
{
136+
"name": author.split("<")[0].strip(),
137+
"email": author.split("<")[1].strip(">"),
138+
}
139+
for author in project_config["project"]["authors"]
140+
],
89141
"license": {"text": project_config["project"]["license"]},
90142
"readme": project_config["project"]["readme"],
91-
"requires-python": f">={project_config['project']['python_version']}",
143+
"requires-python": f">={project_config['project']['python_version']}, <{bump_semver(project_config['project']['python_version'], 'minor')}",
92144
"classifiers": project_config["project"]["classifiers"],
93145
}
94-
146+
95147
# Add project URLs
96-
pyproject["project"]["urls"] = {"Homepage": project_config["project"]["homepage"]}
97-
148+
pyproject["project"]["urls"] = {
149+
"Homepage": project_config["project"]["homepage"]
150+
}
151+
98152
# Add dependencies
99153
pyproject["project"]["dependencies"] = []
100154
for name, version in dependencies["dependencies"].items():
101155
pyproject["project"]["dependencies"].append(f"{name}>={version}")
102-
156+
103157
# Add dev dependencies
104158
pyproject["project"]["optional-dependencies"] = {"dev": []}
105159
for name, version in dependencies["dev-dependencies"].items():
106160
if isinstance(version, dict):
107161
# Handle complex dependencies
108162
ver_str = version.get("version", "*")
109163
if "extras" in version:
110-
extras = ','.join(version["extras"])
111-
pyproject["project"]["optional-dependencies"]["dev"].append(f"{name}[{extras}]>={ver_str}")
164+
extras = ",".join(version["extras"])
165+
pyproject["project"]["optional-dependencies"]["dev"].append(
166+
f"{name}[{extras}]>={ver_str}"
167+
)
112168
else:
113-
pyproject["project"]["optional-dependencies"]["dev"].append(f"{name}>={ver_str}")
169+
pyproject["project"]["optional-dependencies"]["dev"].append(
170+
f"{name}>={ver_str}"
171+
)
114172
else:
115173
if version == "*":
116174
pyproject["project"]["optional-dependencies"]["dev"].append(name)
117175
else:
118-
pyproject["project"]["optional-dependencies"]["dev"].append(f"{name}>={version}")
119-
176+
pyproject["project"]["optional-dependencies"]["dev"].append(
177+
f"{name}>={version}"
178+
)
179+
120180
elif package_manager == "pixi":
121181
# Pixi uses tool.pixi for dependencies
122182
pyproject["project"] = {
123183
"name": project_config["project"]["name"],
124184
"version": project_config["project"]["version"],
125185
"description": project_config["project"]["description"],
126-
"authors": [{"name": author.split("<")[0].strip(), "email": author.split("<")[1].strip(">")}
127-
for author in project_config["project"]["authors"]],
186+
"authors": [
187+
{
188+
"name": author.split("<")[0].strip(),
189+
"email": author.split("<")[1].strip(">"),
190+
}
191+
for author in project_config["project"]["authors"]
192+
],
128193
"license": {"text": project_config["project"]["license"]},
129194
"readme": project_config["project"]["readme"],
130-
"requires-python": f">={project_config['project']['python_version']}",
195+
"requires-python": f">={project_config['project']['python_version']}, <{bump_semver(project_config['project']['python_version'], 'minor')}",
131196
"classifiers": project_config["project"]["classifiers"],
132197
}
133-
198+
134199
# Add project URLs
135-
pyproject["project"]["urls"] = {"Homepage": project_config["project"]["homepage"]}
136-
200+
pyproject["project"]["urls"] = {
201+
"Homepage": project_config["project"]["homepage"]
202+
}
203+
137204
# Add dependencies
138205
pyproject["tool"] = pyproject.get("tool", {})
139206
pyproject["tool"]["pixi"] = {}
140207
pyproject["tool"]["pixi"]["dependencies"] = {}
141208
for name, version in dependencies["dependencies"].items():
142209
pyproject["tool"]["pixi"]["dependencies"][name] = f">={version}"
143-
210+
144211
# Add dev dependencies
145212
pyproject["tool"]["pixi"]["dev-dependencies"] = {}
146213
for name, version in dependencies["dev-dependencies"].items():
147214
if isinstance(version, dict):
148215
# Handle complex dependencies
149216
pyproject["tool"]["pixi"]["dev-dependencies"][name] = {
150217
"version": f">={version['version']}",
151-
"extras": version.get("extras", [])
218+
"extras": version.get("extras", []),
152219
}
153220
else:
154-
pyproject["tool"]["pixi"]["dev-dependencies"][name] = "*" if version == "*" else f">={version}"
155-
221+
pyproject["tool"]["pixi"]["dev-dependencies"][name] = (
222+
"*" if version == "*" else f">={version}"
223+
)
224+
156225
# Write the updated pyproject.toml
157226
with open("pyproject.toml", "wb") as f:
158227
tomli_w.dump(pyproject, f)
159-
228+
160229
# Clean up the template files
161230
remove("project_config.toml")
162231
remove("dependencies.toml")
@@ -176,12 +245,14 @@ def update_pyproject_toml():
176245
update_pyproject_toml()
177246

178247
# Initialize git repository
179-
return_code = os.system("""
248+
return_code = os.system(
249+
"""
180250
echo "Initializing your new project in $(pwd)."
181251
182252
git init
183253
"""
184254
)
185255
if return_code:
186256
import sys
257+
187258
sys.exit(return_code)

0 commit comments

Comments
 (0)