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