2
2
3
3
import warnings
4
4
5
+ from dataclasses import dataclass
5
6
from pathlib import Path
6
- from typing import NamedTuple
7
7
from typing import Sequence
8
8
9
9
from .. import _log
10
- from .setuptools import read_dist_name_from_setup_cfg
10
+ from .. _requirement_cls import extract_package_name
11
11
from .toml import TOML_RESULT
12
12
from .toml import read_toml_content
13
13
16
16
_ROOT = "root"
17
17
18
18
19
- class PyProjectData (NamedTuple ):
19
+ @dataclass
20
+ class PyProjectData :
20
21
path : Path
21
22
tool_name : str
22
23
project : TOML_RESULT
23
24
section : TOML_RESULT
24
25
is_required : bool
25
26
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
+ )
26
52
27
53
@property
28
54
def project_name (self ) -> str | None :
@@ -33,6 +59,10 @@ def verify_dynamic_version_when_required(self) -> None:
33
59
if self .is_required and not self .section_present :
34
60
# When setuptools-scm is in build-system.requires but no tool section exists,
35
61
# 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
36
66
dynamic = self .project .get ("dynamic" , [])
37
67
if "version" not in dynamic :
38
68
raise ValueError (
@@ -43,29 +73,41 @@ def verify_dynamic_version_when_required(self) -> None:
43
73
44
74
45
75
def has_build_package (
46
- requires : Sequence [str ], build_package_names : Sequence [ str ]
76
+ requires : Sequence [str ], canonical_build_package_name : str
47
77
) -> bool :
48
78
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 :
56
81
return True
57
82
return False
58
83
59
84
60
85
def read_pyproject (
61
86
path : Path = Path ("pyproject.toml" ),
62
87
tool_name : str = "setuptools_scm" ,
63
- build_package_names : Sequence [ str ] = ( "setuptools_scm" , " setuptools-scm") ,
88
+ canonical_build_package_name : str = " setuptools-scm" ,
64
89
missing_section_ok : bool = False ,
90
+ missing_file_ok : bool = False ,
65
91
) -> 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
+
67
109
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 )
69
111
70
112
try :
71
113
section = defn .get ("tool" , {})[tool_name ]
@@ -87,8 +129,9 @@ def read_pyproject(
87
129
section_present = False
88
130
89
131
project = defn .get ("project" , {})
132
+ project_present = "project" in defn
90
133
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
92
135
)
93
136
94
137
# Verify dynamic version when setuptools-scm is used as build dependency indicator
@@ -121,8 +164,6 @@ def get_args_for_pyproject(
121
164
if dist_name is None :
122
165
# minimal pep 621 support for figuring the pretend keys
123
166
dist_name = pyproject .project_name
124
- if dist_name is None :
125
- dist_name = read_dist_name_from_setup_cfg ()
126
167
if _ROOT in kwargs :
127
168
if kwargs [_ROOT ] is None :
128
169
kwargs .pop (_ROOT , None )
0 commit comments