23
23
import argparse
24
24
import os
25
25
import os .path
26
- from pathlib import Path
27
26
import shutil
28
27
import subprocess
29
28
import sys
30
29
import tempfile
30
+ from pathlib import Path
31
31
from textwrap import dedent
32
32
from typing import Optional
33
33
@@ -128,6 +128,30 @@ def __init__(self, typeshed_dir: str, distribution: str) -> None:
128
128
self .stub_dir = Path (typeshed_dir ) / THIRD_PARTY_NAMESPACE / distribution
129
129
130
130
131
+ class PackageData :
132
+ """Information about the packages of a distribution and their contents."""
133
+
134
+ def __init__ (self , base_path : Path , package_data : dict [str , list [str ]]) -> None :
135
+ self .base_path = base_path
136
+ self .package_data = package_data
137
+
138
+ @property
139
+ def top_level_packages (self ) -> list [str ]:
140
+ """Top level package names.
141
+
142
+ These are the packages that are not subpackages of any other package
143
+ and includes namespace packages.
144
+ """
145
+ return list (self .package_data .keys ())
146
+
147
+ def add_file (self , package : str , filename : str , file_contents : str ) -> None :
148
+ """Add a file to a package."""
149
+ entry_path = self .base_path / package
150
+ entry_path .mkdir (exist_ok = True )
151
+ (entry_path / filename ).write_text (file_contents )
152
+ self .package_data [package ].append (filename )
153
+
154
+
131
155
def find_stub_files (top : str ) -> list [str ]:
132
156
"""Find all stub files for a given package, relative to package root.
133
157
@@ -197,49 +221,44 @@ def copy_changelog(distribution: str, dst: str) -> None:
197
221
pass # Ignore missing changelogs
198
222
199
223
200
- def collect_setup_entries ( base_dir : str ) -> dict [ str , list [ str ]] :
224
+ def collect_package_data ( base_path : Path ) -> PackageData :
201
225
"""Generate package data for a setuptools.setup() call.
202
226
203
227
This reflects the transformations done during copying in copy_stubs().
204
228
"""
205
229
package_data : dict [str , list [str ]] = {}
206
- for entry in os . listdir ( base_dir ):
207
- if entry == META :
230
+ for entry in base_path . iterdir ( ):
231
+ if entry . name == META :
208
232
# Metadata file entry is added at the end.
209
233
continue
210
- original_entry = entry
211
- if os .path .isfile (os .path .join (base_dir , entry )):
212
- if not entry .endswith (".pyi" ):
213
- if not entry .endswith ((".md" , ".rst" )):
234
+ if entry .is_file ():
235
+ if entry .suffix != ".pyi" :
236
+ if entry .suffix not in (".md" , ".rst" ):
214
237
if (
215
238
subprocess .run (
216
- ["git" , "check-ignore" , entry ], cwd = base_dir
239
+ ["git" , "check-ignore" , entry . name ], cwd = str ( base_path )
217
240
).returncode
218
241
!= 0
219
242
):
220
- raise ValueError (f"Only stub files are allowed, not { entry !r} " )
243
+ raise ValueError (
244
+ f"Only stub files are allowed, not { entry .name !r} "
245
+ )
221
246
continue
222
- entry = entry .split ( "." )[ 0 ] + SUFFIX
247
+ pkg_name = entry .stem + SUFFIX
223
248
# Module -> package transformation is done while copying.
224
- package_data [entry ] = ["__init__.pyi" ]
249
+ package_data [pkg_name ] = ["__init__.pyi" ]
225
250
else :
226
- if entry == TESTS_NAMESPACE :
251
+ if entry . name == TESTS_NAMESPACE :
227
252
continue
228
- entry += SUFFIX
229
- package_data [entry ] = find_stub_files (
230
- os .path .join (base_dir , original_entry )
231
- )
232
- package_data [entry ].append (META )
233
- return package_data
253
+ pkg_name = entry .name + SUFFIX
254
+ package_data [pkg_name ] = find_stub_files (str (entry ))
255
+ package_data [pkg_name ].append (META )
256
+ return PackageData (base_path , package_data )
234
257
235
258
236
- def add_partial_marker (package_data : dict [str , list [str ]], stub_dir : str ) -> None :
237
- for entry , files in package_data .items ():
238
- entry_path = os .path .join (stub_dir , entry )
239
- os .makedirs (entry_path , exist_ok = True )
240
- with open (os .path .join (entry_path , "py.typed" ), "w" ) as py_typed :
241
- py_typed .write ("partial\n " )
242
- files .append ("py.typed" )
259
+ def add_partial_markers (pkg_data : PackageData ) -> None :
260
+ for package in pkg_data .top_level_packages :
261
+ pkg_data .add_file (package , "py.typed" , "partial\n " )
243
262
244
263
245
264
def generate_setup_file (
@@ -253,9 +272,9 @@ def generate_setup_file(
253
272
all_requirements = [
254
273
str (req ) for req in metadata .requires_typeshed + metadata .requires_external
255
274
]
256
- package_data = collect_setup_entries ( str ( build_data .stub_dir ) )
275
+ pkg_data = collect_package_data ( build_data .stub_dir )
257
276
if metadata .partial :
258
- add_partial_marker ( package_data , str ( build_data . stub_dir ) )
277
+ add_partial_markers ( pkg_data )
259
278
requires_python = (
260
279
metadata .requires_python
261
280
if metadata .requires_python is not None
@@ -269,8 +288,8 @@ def generate_setup_file(
269
288
),
270
289
version = version ,
271
290
requires = all_requirements ,
272
- packages = list ( package_data . keys ()) ,
273
- package_data = package_data ,
291
+ packages = pkg_data . top_level_packages ,
292
+ package_data = pkg_data . package_data ,
274
293
requires_python = requires_python ,
275
294
)
276
295
0 commit comments