Skip to content

Commit afce7bf

Browse files
xiangyan99lzchenazure-sdkmsyycelliotzh
authored
add snippet update step (#26899)
* add snippet update step * Get latest main (#26934) * Change default local storage location (#26891) * code and test (#26908) Co-authored-by: PythonSdkPipelines <PythonSdkPipelines> * Update swagger_to_sdk_config_dpg.json (#26913) * feat: enable internal components in pipeline yaml (#26800) * feat: enable internal components in pipeline yaml * refactor: add some gate logic * fix: fix test_pipeline_job_create_with_registries * fix: fix pylint * Cadl automation pipeline (#26912) * cdoe * fix * code * fix * code * meta.json * meta.json * compatible * refactor: divide pipeline related tests (#26886) * fix: sdk.ml.azure-ai-ml.tests.datastore.e2etests.test_datastore * feat: component with default label * refactor: divide test_dsl_pipeline.py * feat: load labelled arm id * feat: support name@default use name@label for node component in pipeline yaml * [TA ] improve per-document error message when all documents fail per action (#26902) * fix bad Document error message when all actions fail * update changelog * update wording * skip cosmos emulator (#26930) * try 3.7 * skip cosmos emulator tests until fixed * revert change * comment out emulator * fix ci for core (#26923) * fix ci for core * updates * updates * updates * updates * updates * adding additional version override support * update * update * update * update conda test-requirements to include azure-mgmt-storage that was previously coming dependency tools/azure-sdk-tools Co-authored-by: scbedd <[email protected]> Co-authored-by: Leighton Chen <[email protected]> Co-authored-by: Azure SDK Bot <[email protected]> Co-authored-by: Yuchao Yan <[email protected]> Co-authored-by: Xingzhi Zhang <[email protected]> Co-authored-by: Krista Pratico <[email protected]> Co-authored-by: scbedd <[email protected]> * update * update * update * update * update * Update update_snippet.yml * Update eng/pipelines/templates/steps/analyze.yml Co-authored-by: Wes Haggard <[email protected]> * Update update_snippet.yml * update * update * update * updates * update * Updates * updates * updates * update * Add comments Co-authored-by: Leighton Chen <[email protected]> Co-authored-by: Azure SDK Bot <[email protected]> Co-authored-by: Yuchao Yan <[email protected]> Co-authored-by: Xingzhi Zhang <[email protected]> Co-authored-by: Krista Pratico <[email protected]> Co-authored-by: scbedd <[email protected]> Co-authored-by: Wes Haggard <[email protected]>
1 parent 2cb703c commit afce7bf

File tree

9 files changed

+1203
-0
lines changed

9 files changed

+1203
-0
lines changed

eng/pipelines/templates/steps/analyze.yml

+5
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ steps:
115115
TestMarkArgument: ${{ parameters.TestMarkArgument }}
116116
AdditionalTestArgs: ${{parameters.AdditionalTestArgs}}
117117

118+
- template: /eng/pipeines/templates/steps/update_snippet.yml
119+
parameters:
120+
ScanPath: $(Build.SourcesDirectory)/sdk/${{ parameters.ServiceDirectory }}
121+
AdditionalTestArgs: ${{parameters.AdditionalTestArgs}}
122+
118123
- template: ../steps/run_breaking_changes.yml
119124
parameters:
120125
ServiceDirectory: ${{ parameters.ServiceDirectory }}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
parameters:
2+
ServiceDirectory: ''
3+
ValidateFormatting: false
4+
EnvVars: {}
5+
6+
steps:
7+
- task: UsePythonVersion@0
8+
displayName: 'Use Python 3.9'
9+
inputs:
10+
versionSpec: '3.9'
11+
condition: succeededOrFailed()
12+
13+
- task: PythonScript@0
14+
displayName: 'Update Snippets'
15+
inputs:
16+
scriptPath: 'tools/azure-sdk-tools/ci_tools/snippet_update/python_snippet_updater.py'
17+
arguments: >-
18+
${{ parameters.ScanPath }}
19+
condition: and(succeededOrFailed(), ne(variables['Skip.UpdateSnippet'],'true'))

tools/azure-sdk-tools/ci_tools/snippet_update/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import sys
2+
import logging
3+
from pathlib import Path
4+
import argparse
5+
import re
6+
from typing import Dict
7+
8+
_LOGGER = logging.getLogger(__name__)
9+
10+
snippets = {}
11+
not_up_to_date = False
12+
13+
target_snippet_sources = ["samples/*.py", "samples/*/*.py"]
14+
target_md_files = ["README.md"]
15+
16+
def check_snippets() -> Dict:
17+
return snippets
18+
19+
def check_not_up_to_date() -> bool:
20+
return not_up_to_date
21+
22+
def get_snippet(file: str) -> None:
23+
file_obj = Path(file)
24+
with open(file_obj, 'r') as f:
25+
content = f.read()
26+
pattern = "# \\[START(?P<name>[A-Z a-z0-9_]+)\\](?P<body>[\\s\\S]+?)# \\[END[A-Z a-z0-9_]+\\]"
27+
matches = re.findall(pattern, content)
28+
for match in matches:
29+
s = match
30+
name = s[0].strip()
31+
snippet = s[1]
32+
# Remove extra spaces
33+
# A sample code snippet could be like:
34+
# \n
35+
# # [START trio]
36+
# from azure.core.pipeline.transport import TrioRequestsTransport
37+
38+
# async with AsyncPipeline(TrioRequestsTransport(), policies=policies) as pipeline:
39+
# return await pipeline.run(request)
40+
# # [END trio]
41+
# \n
42+
# On one hand, the spaces in the beginning of the line may vary. e.g. If the snippet
43+
# is in a class, it may have more spaces than if it is not in a class.
44+
# On the other hand, we cannot remove all spaces because indents are part of Python syntax.
45+
# Here is our algorithm:
46+
# We firstly count the spaces of the # [START snippet] line.
47+
# And for every line, we remove this amount of spaces in the beginning of the line.
48+
# To only remove the spaces in the beginning and to make sure we only remove it once per line,
49+
# We use replace('\n' + spaces, '\n').
50+
spaces = ""
51+
for char in snippet[1:]:
52+
if char == " ":
53+
spaces += char
54+
else:
55+
break
56+
snippet = snippet.replace("\n" + spaces, "\n")
57+
# Remove first newline
58+
snippet = snippet[1:].rstrip()
59+
if snippet[-1] == "\n":
60+
snippet = snippet[:-1]
61+
62+
file_name = str(file_obj.name)[:-3]
63+
identifier = ".".join([file_name, name])
64+
if identifier in snippets.keys():
65+
_LOGGER.warning(f'Found duplicated snippet name "{identifier}".')
66+
_LOGGER.warning(file)
67+
_LOGGER.debug(f"Found: {file_obj.name}.{name}")
68+
snippets[identifier] = snippet
69+
70+
71+
def update_snippet(file: str) -> None:
72+
file_obj = Path(file)
73+
with open(file_obj, 'r') as f:
74+
content = f.read()
75+
pattern = "(?P<content>(?P<header><!-- SNIPPET:(?P<name>[A-Z a-z0-9_.]+)-->)\\n```python\\n[\\s\\S]*?\\n<!-- END SNIPPET -->)"
76+
matches = re.findall(pattern, content)
77+
for match in matches:
78+
s = match
79+
body = s[0].strip()
80+
header = s[1].strip()
81+
name = s[2].strip()
82+
_LOGGER.debug(f"Found name: {name}")
83+
if name not in snippets.keys():
84+
_LOGGER.error(f'In {file}, failed to found snippet name "{name}".')
85+
exit(1)
86+
target_code = "".join([header, "\n```python\n", snippets[name], "\n```\n", "<!-- END SNIPPET -->"])
87+
if body != target_code:
88+
_LOGGER.warning(f'Snippet "{name}" is not up to date.')
89+
global not_up_to_date
90+
not_up_to_date = True
91+
content = content.replace(body, target_code)
92+
with open(file_obj, 'w') as f:
93+
f.write(content)
94+
95+
96+
if __name__ == "__main__":
97+
parser = argparse.ArgumentParser()
98+
parser.add_argument(
99+
"path",
100+
nargs="?",
101+
help=(
102+
"The targeted path for update."
103+
),
104+
)
105+
args = parser.parse_args()
106+
path = sys.argv[1]
107+
_LOGGER.info(f"Path: {path}")
108+
for source in target_snippet_sources:
109+
for py_file in Path(path).rglob(source):
110+
try:
111+
get_snippet(py_file)
112+
except UnicodeDecodeError:
113+
pass
114+
for key in snippets.keys():
115+
_LOGGER.debug(f"Found snippet: {key}")
116+
for target in target_md_files:
117+
for md_file in Path(path).rglob(target):
118+
try:
119+
update_snippet(md_file)
120+
except UnicodeDecodeError:
121+
pass
122+
if not_up_to_date:
123+
_LOGGER.error(f'Error: code snippets are out of sync. Please run Python PythonSnippetUpdater.py "{path}" to fix it.')
124+
exit(1)
125+
_LOGGER.info(f"README.md under {path} is up to date.")

0 commit comments

Comments
 (0)