1111import os
1212import glob
1313import shutil
14+ from unicodedata import name
15+ from xmlrpc .client import Boolean
1416from tox_helper_tasks import (
1517 get_package_details ,
1618 unzip_file_to_directory ,
1719)
1820from verify_whl import cleanup , should_verify_package
21+ from typing import List , Mapping , Any
1922
2023logging .getLogger ().setLevel (logging .INFO )
2124
2225ALLOWED_ROOT_DIRECTORIES = ["azure" , "tests" , "samples" , "examples" ]
2326
27+ EXCLUDED_PYTYPE_PACKAGES = ["azure-keyvault" , "azure" , "azure-common" ]
2428
25- def get_root_directories_in_source (package_dir ):
26- # find all allowed directories in source path
27- source_folders = [
28- d
29- for d in os .listdir (package_dir )
30- if os .path .isdir (d ) and d in ALLOWED_ROOT_DIRECTORIES
31- ]
29+
30+ def get_root_directories_in_source (package_dir : str ) -> List [str ]:
31+ """
32+ Find all allowed directories in source path.
33+ """
34+ source_folders = [d for d in os .listdir (package_dir ) if os .path .isdir (d ) and d in ALLOWED_ROOT_DIRECTORIES ]
3235 return source_folders
3336
3437
35- def get_root_directories_in_sdist (dist_dir , version ):
38+ def get_root_directories_in_sdist (dist_dir : str , version : str ) -> List [str ]:
39+ """
40+ Given an unzipped sdist directory, extract which directories are present.
41+ """
3642 # find sdist zip file
3743 # extract sdist and find list of directories in sdist
3844 path_to_zip = glob .glob (os .path .join (dist_dir , "*{}*.zip" .format (version )))[0 ]
@@ -44,8 +50,10 @@ def get_root_directories_in_sdist(dist_dir, version):
4450 return sdist_folders
4551
4652
47- def verify_sdist (package_dir , dist_dir , version ):
48- # This function compares the root directories in source against root directories sdist
53+ def verify_sdist (package_dir : str , dist_dir : str , version : str ) -> bool :
54+ """
55+ Compares the root directories in source against root directories present within a sdist.
56+ """
4957
5058 source_folders = get_root_directories_in_source (package_dir )
5159 sdist_folders = get_root_directories_in_sdist (dist_dir , version )
@@ -63,9 +71,49 @@ def verify_sdist(package_dir, dist_dir, version):
6371 return True
6472
6573
74+ def verify_sdist_pytyped (
75+ pkg_dir : str , namespace : str , package_metadata : Mapping [str , Any ], include_package_data : bool
76+ ) -> bool :
77+ """
78+ Takes a package directory and ensures that the setup.py within is correctly configured for py.typed files.
79+ """
80+ result = True
81+ manifest_location = os .path .join (pkg_dir , "MANIFEST.in" )
82+
83+ if include_package_data is None or False :
84+ logging .info (
85+ "Ensure that the setup.py present in directory {} has kwarg 'include_package_data' defined and set to 'True'."
86+ )
87+ result = False
88+
89+ if package_metadata :
90+ if not any ([key for key in package_metadata if "py.typed" in str (package_metadata [key ])]):
91+ logging .info (
92+ "At least one value in the package_metadata map should include a reference to the py.typed file."
93+ )
94+ result = False
95+
96+ if os .path .exists (manifest_location ):
97+ with open (manifest_location , "r" ) as f :
98+ lines = f .readlines ()
99+ result = any ([include for include in lines if "py.typed" in include ])
100+
101+ if not result :
102+ logging .info ("Ensure that the MANIFEST.in includes at least one path that leads to a py.typed file." )
103+
104+ pytyped_file_path = os .path .join (pkg_dir , * namespace .split ("." ), "py.typed" )
105+ if not os .path .exists (pytyped_file_path ):
106+ logging .info (
107+ "The py.typed file must exist in the base namespace for your package. Traditionally this would mean the furthest depth, EG 'azure/storage/blob/py.typed'."
108+ )
109+ result = False
110+
111+ return result
112+
113+
66114if __name__ == "__main__" :
67115 parser = argparse .ArgumentParser (
68- description = "Verify directories included in whl and contents in manifest file"
116+ description = "Verify directories included in sdist and contents in manifest file. Also ensures that py.typed configuration is correct within the setup.py. "
69117 )
70118
71119 parser .add_argument (
@@ -88,7 +136,9 @@ def verify_sdist(package_dir, dist_dir, version):
88136
89137 # get target package name from target package path
90138 pkg_dir = os .path .abspath (args .target_package )
91- pkg_name , _ , ver = get_package_details (os .path .join (pkg_dir , "setup.py" ))
139+ pkg_name , namespace , ver , package_data , include_package_data = get_package_details (
140+ os .path .join (pkg_dir , "setup.py" )
141+ )
92142
93143 if should_verify_package (pkg_name ):
94144 logging .info ("Verifying sdist for package [%s]" , pkg_name )
@@ -97,3 +147,11 @@ def verify_sdist(package_dir, dist_dir, version):
97147 else :
98148 logging .info ("Failed to verify sdist for package [%s]" , pkg_name )
99149 exit (1 )
150+
151+ if pkg_name not in EXCLUDED_PYTYPE_PACKAGES and "-nspkg" not in pkg_name :
152+ logging .info ("Verifying presence of py.typed: [%s]" , pkg_name )
153+ if verify_sdist_pytyped (pkg_dir , namespace , package_data , include_package_data ):
154+ logging .info ("Py.typed setup.py kwargs are set properly: [%s]" , pkg_name )
155+ else :
156+ logging .info ("Verified py.typed [%s]. Check messages above." , pkg_name )
157+ exit (1 )
0 commit comments