@@ -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+
3061def 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():
176245update_pyproject_toml ()
177246
178247# Initialize git repository
179- return_code = os .system ("""
248+ return_code = os .system (
249+ """
180250echo "Initializing your new project in $(pwd)."
181251
182252git init
183253"""
184254)
185255if return_code :
186256 import sys
257+
187258 sys .exit (return_code )
0 commit comments