22
33import warnings
44
5+ from dataclasses import dataclass
56from pathlib import Path
6- from typing import NamedTuple
77from typing import Sequence
88
99from .. import _log
10- from .setuptools import read_dist_name_from_setup_cfg
10+ from .. _requirement_cls import extract_package_name
1111from .toml import TOML_RESULT
1212from .toml import read_toml_content
1313
1616_ROOT = "root"
1717
1818
19- class PyProjectData (NamedTuple ):
19+ @dataclass
20+ class PyProjectData :
2021 path : Path
2122 tool_name : str
2223 project : TOML_RESULT
2324 section : TOML_RESULT
2425 is_required : bool
2526 section_present : bool
27+ project_present : bool
28+
29+ @classmethod
30+ def for_testing (
31+ cls ,
32+ is_required : bool = False ,
33+ section_present : bool = False ,
34+ project_present : bool = False ,
35+ project_name : str | None = None ,
36+ ) -> PyProjectData :
37+ """Create a PyProjectData instance for testing purposes."""
38+ if project_name is not None :
39+ project = {"name" : project_name }
40+ assert project_present
41+ else :
42+ project = {}
43+ return cls (
44+ path = Path ("pyproject.toml" ),
45+ tool_name = "setuptools_scm" ,
46+ project = project ,
47+ section = {},
48+ is_required = is_required ,
49+ section_present = section_present ,
50+ project_present = project_present ,
51+ )
2652
2753 @property
2854 def project_name (self ) -> str | None :
@@ -33,6 +59,10 @@ def verify_dynamic_version_when_required(self) -> None:
3359 if self .is_required and not self .section_present :
3460 # When setuptools-scm is in build-system.requires but no tool section exists,
3561 # we need to verify that dynamic=['version'] is set in the project section
62+ # But only if there's actually a project section
63+ if not self .project_present :
64+ # No project section, so don't auto-activate setuptools_scm
65+ return
3666 dynamic = self .project .get ("dynamic" , [])
3767 if "version" not in dynamic :
3868 raise ValueError (
@@ -43,29 +73,41 @@ def verify_dynamic_version_when_required(self) -> None:
4373
4474
4575def has_build_package (
46- requires : Sequence [str ], build_package_names : Sequence [ str ]
76+ requires : Sequence [str ], canonical_build_package_name : str
4777) -> bool :
4878 for requirement in requires :
49- import re
50-
51- # Remove extras like [toml] first
52- clean_req = re .sub (r"\[.*?\]" , "" , requirement )
53- # Split on version operators and take first part
54- package_name = re .split (r"[><=!~]" , clean_req )[0 ].strip ().lower ()
55- if package_name in build_package_names :
79+ package_name = extract_package_name (requirement )
80+ if package_name == canonical_build_package_name :
5681 return True
5782 return False
5883
5984
6085def read_pyproject (
6186 path : Path = Path ("pyproject.toml" ),
6287 tool_name : str = "setuptools_scm" ,
63- build_package_names : Sequence [ str ] = ( "setuptools_scm" , " setuptools-scm") ,
88+ canonical_build_package_name : str = " setuptools-scm" ,
6489 missing_section_ok : bool = False ,
90+ missing_file_ok : bool = False ,
6591) -> PyProjectData :
66- defn = read_toml_content (path )
92+ try :
93+ defn = read_toml_content (path )
94+ except FileNotFoundError :
95+ if missing_file_ok :
96+ log .warning ("File %s not found, using empty configuration" , path )
97+ return PyProjectData (
98+ path = path ,
99+ tool_name = tool_name ,
100+ project = {},
101+ section = {},
102+ is_required = False ,
103+ section_present = False ,
104+ project_present = False ,
105+ )
106+ else :
107+ raise
108+
67109 requires : list [str ] = defn .get ("build-system" , {}).get ("requires" , [])
68- is_required = has_build_package (requires , build_package_names )
110+ is_required = has_build_package (requires , canonical_build_package_name )
69111
70112 try :
71113 section = defn .get ("tool" , {})[tool_name ]
@@ -87,8 +129,9 @@ def read_pyproject(
87129 section_present = False
88130
89131 project = defn .get ("project" , {})
132+ project_present = "project" in defn
90133 pyproject_data = PyProjectData (
91- path , tool_name , project , section , is_required , section_present
134+ path , tool_name , project , section , is_required , section_present , project_present
92135 )
93136
94137 # Verify dynamic version when setuptools-scm is used as build dependency indicator
@@ -121,8 +164,6 @@ def get_args_for_pyproject(
121164 if dist_name is None :
122165 # minimal pep 621 support for figuring the pretend keys
123166 dist_name = pyproject .project_name
124- if dist_name is None :
125- dist_name = read_dist_name_from_setup_cfg ()
126167 if _ROOT in kwargs :
127168 if kwargs [_ROOT ] is None :
128169 kwargs .pop (_ROOT , None )
0 commit comments