Skip to content

Commit 046ba0c

Browse files
committed
update changelog level
1 parent e828338 commit 046ba0c

1 file changed

Lines changed: 93 additions & 96 deletions

File tree

scripts/breaking_changes_checker/detect_breaking_changes.py

Lines changed: 93 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
class _ClassNodeFound(Exception):
3535
"""Raised to short-circuit AST traversal when the target class is found."""
36+
3637
pass
3738

3839

@@ -97,9 +98,7 @@ def test_find_modules(pkg_root_path: str) -> Dict:
9798

9899
# Add current path as module name if _init.py is present
99100
if "__init__.py" in files:
100-
module_name = os.path.relpath(root, pkg_root_path).replace(
101-
os.path.sep, "."
102-
)
101+
module_name = os.path.relpath(root, pkg_root_path).replace(os.path.sep, ".")
103102
modules[module_name] = []
104103
for f in files:
105104
if f.endswith(".py"):
@@ -179,22 +178,17 @@ def get_property_names(node: ast.AST, attribute_names: Dict) -> None:
179178
if hasattr(assign, "target"):
180179
if hasattr(assign.target, "attr") and not assign.target.attr.startswith("_"):
181180
attr = assign.target
182-
attribute_names.update({attr.attr: {
183-
"attr_type": get_property_type(assign)
184-
}})
181+
attribute_names.update({attr.attr: {"attr_type": get_property_type(assign)}})
185182
if hasattr(assign, "targets"):
186183
for target in assign.targets:
187184
if hasattr(target, "attr") and not target.attr.startswith("_"):
188-
attribute_names.update({target.attr: {
189-
"attr_type": get_property_type(assign)
190-
}})
185+
attribute_names.update({target.attr: {"attr_type": get_property_type(assign)}})
191186

192187

193188
def check_base_classes(cls_node: ast.ClassDef) -> bool:
194189
should_look = False
195190
init_node = [
196-
node for node in cls_node.body
197-
if isinstance(node, ast.FunctionDef) and node.name.startswith("__init__")
191+
node for node in cls_node.body if isinstance(node, ast.FunctionDef) and node.name.startswith("__init__")
198192
]
199193
if init_node:
200194
if hasattr(init_node, "body"):
@@ -231,26 +225,23 @@ def get_properties(cls: Type) -> Dict:
231225
path = inspect.getsourcefile(base_class)
232226
module = _get_parsed_module(path)
233227
except (TypeError, SyntaxError):
234-
_LOGGER.info(f"Unable to create ast of {base_class}")
228+
_LOGGER.debug(f"Unable to create ast of {base_class}")
235229
continue # was a built-in, e.g. "object", Exception, or a Model from msrest fails here
236230

237231
cls_node = _find_class_node(module, base_class.__name__)
238232
if cls_node:
239233
get_property_names(cls_node, attribute_names)
240234
else:
241235
# Abstract base classes fail here, e.g. "collections.abc.MuttableMapping"
242-
_LOGGER.info(f"Unable to get class node for {base_class.__name__}. Skipping...")
236+
_LOGGER.debug(f"Unable to get class node for {base_class.__name__}. Skipping...")
243237
else:
244238
get_property_names(cls_node, attribute_names)
245239
return attribute_names
246240

247241

248242
def create_function_report(f: Callable, is_async: bool = False) -> Dict:
249243
function = inspect.signature(f)
250-
func_obj = {
251-
"parameters": {},
252-
"is_async": is_async
253-
}
244+
func_obj = {"parameters": {}, "is_async": is_async}
254245

255246
for par in function.parameters.values():
256247
default_value = get_parameter_default(par)
@@ -308,46 +299,55 @@ def create_parameters(args: ast.arg) -> Dict:
308299
if hasattr(args, "posonlyargs"):
309300
for arg in args.posonlyargs:
310301
# Initialize the function parameters
311-
params.update({arg.arg: {
312-
"type": get_parameter_type(arg.annotation),
313-
"default": None,
314-
"param_type": "positional_only"
315-
}})
302+
params.update(
303+
{
304+
arg.arg: {
305+
"type": get_parameter_type(arg.annotation),
306+
"default": None,
307+
"param_type": "positional_only",
308+
}
309+
}
310+
)
316311
if hasattr(args, "args"):
317312
for arg in args.args:
318313
# Initialize the function parameters
319-
params.update({arg.arg: {
320-
"type": get_parameter_type(arg.annotation),
321-
"default": None,
322-
"param_type": "positional_or_keyword"
323-
}})
314+
params.update(
315+
{
316+
arg.arg: {
317+
"type": get_parameter_type(arg.annotation),
318+
"default": None,
319+
"param_type": "positional_or_keyword",
320+
}
321+
}
322+
)
324323
# Range through the corresponding default values
325324
all_args = args.posonlyargs + args.args
326325
positional_defaults = [None] * (len(all_args) - len(args.defaults)) + args.defaults
327326
for arg, default in zip(all_args, positional_defaults):
328327
params[arg.arg]["default"] = get_parameter_default_ast(default)
329328
if hasattr(args, "vararg"):
330329
if args.vararg:
331-
params.update({args.vararg.arg: {
332-
"type": get_parameter_type(args.vararg.annotation),
333-
"default": None,
334-
"param_type": "var_positional"
335-
}})
330+
params.update(
331+
{
332+
args.vararg.arg: {
333+
"type": get_parameter_type(args.vararg.annotation),
334+
"default": None,
335+
"param_type": "var_positional",
336+
}
337+
}
338+
)
336339
if hasattr(args, "kwonlyargs"):
337340
for arg in args.kwonlyargs:
338341
# Initialize the function parameters
339-
params.update({
340-
arg.arg: {
341-
"type": get_parameter_type(arg.annotation),
342-
"default": None,
343-
"param_type": "keyword_only"
344-
}
345-
})
342+
params.update(
343+
{arg.arg: {"type": get_parameter_type(arg.annotation), "default": None, "param_type": "keyword_only"}}
344+
)
346345
# Range through the corresponding default values
347346
for i in range(len(args.kwonlyargs) - len(args.kw_defaults), len(args.kwonlyargs)):
348347
params[args.kwonlyargs[i].arg]["default"] = get_parameter_default_ast(args.kw_defaults[i])
349348
return params
350349

350+
351351
def get_overloads(cls: Type, cls_methods: Dict):
352352
path = inspect.getsourcefile(cls)
353353
module = _get_parsed_module(path)
@@ -361,22 +361,24 @@ def get_overloads(cls: Type, cls_methods: Dict):
361361
path = inspect.getsourcefile(base_class)
362362
module = _get_parsed_module(path)
363363
except (TypeError, SyntaxError):
364-
_LOGGER.info(f"Unable to create ast of {base_class}")
364+
_LOGGER.debug(f"Unable to create ast of {base_class}")
365365
continue # was a built-in, e.g. "object", Exception, or a Model from msrest fails here
366366

367367
cls_node = _find_class_node(module, base_class.__name__)
368368
if cls_node:
369369
get_overload_data(cls_node, cls_methods)
370370
else:
371371
# Abstract base classes fail here, e.g. "collections.abc.MuttableMapping"
372-
_LOGGER.info(f"Unable to get class node for {base_class.__name__}. Skipping...")
372+
_LOGGER.debug(f"Unable to get class node for {base_class.__name__}. Skipping...")
373373
else:
374374
get_overload_data(cls_node, cls_methods)
375375

376376

377377
def get_overload_data(node: ast.ClassDef, cls_methods: Dict) -> None:
378378
func_nodes = [node for node in node.body if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))]
379-
public_func_nodes = [func for func in func_nodes if not func.name.startswith("_") or func.name.startswith("__init__")]
379+
public_func_nodes = [
380+
func for func in func_nodes if not func.name.startswith("_") or func.name.startswith("__init__")
381+
]
380382
# Check for method overloads on a class
381383
for func in public_func_nodes:
382384
if func.name not in cls_methods:
@@ -393,7 +395,7 @@ def get_overload_data(node: ast.ClassDef, cls_methods: Dict) -> None:
393395
overload_report = {
394396
"parameters": create_parameters(func.args),
395397
"is_async": is_async,
396-
"return_type": None
398+
"return_type": None,
397399
}
398400
cls_methods[func.name]["overloads"].append(overload_report)
399401

@@ -422,7 +424,7 @@ def create_class_report(cls: Type) -> Dict:
422424
except AttributeError:
423425
_LOGGER.info(f"Skipping method check for {method} on {cls}.")
424426
continue
425-
427+
426428
if inspect.isfunction(m) or inspect.ismethod(m):
427429
if inspect.iscoroutinefunction(m):
428430
async_func = True
@@ -464,7 +466,9 @@ def build_library_report(target_module: str) -> Dict:
464466
return public_api
465467

466468

467-
def test_compare_reports(pkg_dir: str, changelog: bool, source_report: str = "stable.json", target_report: str = "current.json") -> None:
469+
def test_compare_reports(
470+
pkg_dir: str, changelog: bool, source_report: str = "stable.json", target_report: str = "current.json"
471+
) -> None:
468472
package_name = os.path.basename(pkg_dir)
469473

470474
# Preserve the original argument values so we can decide later whether cleanup is safe.
@@ -489,18 +493,18 @@ def test_compare_reports(pkg_dir: str, changelog: bool, source_report: str = "st
489493
stable,
490494
current,
491495
package_name,
492-
checkers = CHECKERS,
493-
ignore = IGNORE_BREAKING_CHANGES,
494-
post_processing_checkers = POST_PROCESSING_CHECKERS
496+
checkers=CHECKERS,
497+
ignore=IGNORE_BREAKING_CHANGES,
498+
post_processing_checkers=POST_PROCESSING_CHECKERS,
495499
)
496500
if changelog:
497501
checker = ChangelogTracker(
498502
stable,
499503
current,
500504
package_name,
501-
checkers = CHECKERS,
502-
ignore = IGNORE_BREAKING_CHANGES,
503-
post_processing_checkers = POST_PROCESSING_CHECKERS,
505+
checkers=CHECKERS,
506+
ignore=IGNORE_BREAKING_CHANGES,
507+
post_processing_checkers=POST_PROCESSING_CHECKERS,
504508
)
505509
checker.run_checks()
506510

@@ -531,7 +535,7 @@ def remove_json_files(pkg_dir: str) -> None:
531535

532536

533537
def report_azure_mgmt_versioned_module(code_report):
534-
538+
535539
def parse_module_name(module):
536540
split_module = module.split(".")
537541
# Azure mgmt packages are typically in the form of: azure.mgmt.<service>
@@ -555,17 +559,17 @@ def parse_module_name(module):
555559

556560

557561
def main(
558-
package_name: str,
559-
target_module: str,
560-
version: str,
561-
in_venv: Union[bool, str],
562-
pkg_dir: str,
563-
changelog: bool,
564-
code_report: bool,
565-
latest_pypi_version: bool,
566-
source_report: Optional[Path],
567-
target_report: Optional[Path]
568-
):
562+
package_name: str,
563+
target_module: str,
564+
version: str,
565+
in_venv: Union[bool, str],
566+
pkg_dir: str,
567+
changelog: bool,
568+
code_report: bool,
569+
latest_pypi_version: bool,
570+
source_report: Optional[Path],
571+
target_report: Optional[Path],
572+
):
569573
# If code_report is set, only generate a code report for the package and return
570574
if code_report:
571575
public_api = build_library_report(target_module)
@@ -583,6 +587,7 @@ def main(
583587
if not version:
584588

585589
from pypi_tools.pypi import PyPIClient
590+
586591
client = PyPIClient()
587592

588593
try:
@@ -601,28 +606,10 @@ def main(
601606
packages = [f"{package_name}=={version}", "jsondiff==1.2.0"]
602607
with create_venv_with_package(packages) as venv:
603608
subprocess.check_call(
604-
[
605-
venv.env_exe,
606-
"-m",
607-
"pip",
608-
"install",
609-
"-r",
610-
os.path.join(pkg_dir, "dev_requirements.txt")
611-
]
609+
[venv.env_exe, "-m", "pip", "install", "-r", os.path.join(pkg_dir, "dev_requirements.txt")]
612610
)
613611
_LOGGER.info(f"Installed version {version} of {package_name} in a venv")
614-
args = [
615-
venv.env_exe,
616-
__file__,
617-
"-t",
618-
package_name,
619-
"-m",
620-
target_module,
621-
"--in-venv",
622-
"true",
623-
"-s",
624-
version
625-
]
612+
args = [venv.env_exe, __file__, "-t", package_name, "-m", target_module, "--in-venv", "true", "-s", version]
626613
try:
627614
subprocess.check_call(args)
628615
except subprocess.CalledProcessError:
@@ -650,9 +637,7 @@ def main(
650637

651638

652639
if __name__ == "__main__":
653-
parser = argparse.ArgumentParser(
654-
description="Run breaking changes checks against target folder."
655-
)
640+
parser = argparse.ArgumentParser(description="Run breaking changes checks against target folder.")
656641

657642
parser.add_argument(
658643
"-t",
@@ -670,19 +655,15 @@ def main(
670655
)
671656

672657
parser.add_argument(
673-
"-v",
674-
"--in-venv",
675-
dest="in_venv",
676-
help="Check if we are in the newly created venv.",
677-
default=False
658+
"-v", "--in-venv", dest="in_venv", help="Check if we are in the newly created venv.", default=False
678659
)
679660

680661
parser.add_argument(
681662
"-s",
682663
"--stable_version",
683664
dest="stable_version",
684665
help="The stable version of the target package, if it exists on PyPi.",
685-
default=None
666+
default=None,
686667
)
687668

688669
parser.add_argument(
@@ -736,13 +717,18 @@ def main(
736717

737718
# We dont need to block for code report generation
738719
if not args.code_report:
739-
if package_name not in RUN_BREAKING_CHANGES_PACKAGES and not any(bool(re.findall(p, package_name)) for p in RUN_BREAKING_CHANGES_PACKAGES):
740-
_LOGGER.info(f"{package_name} opted out of breaking changes checks. "
741-
f"See http://aka.ms/azsdk/breaking-changes-tool to opt-in.")
720+
if package_name not in RUN_BREAKING_CHANGES_PACKAGES and not any(
721+
bool(re.findall(p, package_name)) for p in RUN_BREAKING_CHANGES_PACKAGES
722+
):
723+
_LOGGER.info(
724+
f"{package_name} opted out of breaking changes checks. "
725+
f"See http://aka.ms/azsdk/breaking-changes-tool to opt-in."
726+
)
742727
exit(0)
743728

744729
if not target_module and not (args.source_report and args.target_report):
745730
from ci_tools.parsing import ParsedSetup
731+
746732
pkg_details = ParsedSetup.from_path(pkg_dir)
747733
target_module = pkg_details.namespace
748734

@@ -755,4 +741,15 @@ def main(
755741
_LOGGER.exception("If providing the `--target-report` flag, the `--source-report` flag is also required.")
756742
exit(1)
757743

758-
main(package_name, target_module, stable_version, in_venv, pkg_dir, changelog, args.code_report, args.latest_pypi_version, args.source_report, args.target_report)
744+
main(
745+
package_name,
746+
target_module,
747+
stable_version,
748+
in_venv,
749+
pkg_dir,
750+
changelog,
751+
args.code_report,
752+
args.latest_pypi_version,
753+
args.source_report,
754+
args.target_report,
755+
)

0 commit comments

Comments
 (0)