Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

split emitter into autorest.python and pygen #2588

Merged
merged 86 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
c139e14
initial moving over into pygen core
May 10, 2024
f899041
split package into pygen/core and autorest.python
May 13, 2024
213bdd7
black
May 13, 2024
13db1bb
pylint and mypy
May 13, 2024
e96acfa
Merge branch 'main' of https://github.com/Azure/autorest.python into …
May 13, 2024
fa03ccd
add editable install of pygen into autorest.python
May 13, 2024
c0367d4
move files over to pygen
May 13, 2024
93ea493
basic generaiton possible from autorest with script files moved to pygen
May 13, 2024
d2a84ca
autorest generation working from scripts in pygen file
May 13, 2024
ac5126d
require changelog check
May 13, 2024
5e9f023
add changeset
May 13, 2024
721a467
add pcgc to changelog
May 13, 2024
def03ac
comment out pylinting
May 13, 2024
35fee3c
add back linting and check
May 14, 2024
321f1e4
list installed packages
May 14, 2024
2189cd7
Merge branch 'main' of https://github.com/Azure/autorest.python into …
May 14, 2024
a42baf1
list installed packages
May 14, 2024
edc0faf
activate env in pipeline
May 14, 2024
94982aa
add pygen to common reqs
May 14, 2024
5d8f981
change path to be relative from autorest.python
May 14, 2024
99f1d25
pylint
May 14, 2024
b6e0fae
pyright
May 14, 2024
ea73b14
add pygen to setup.py, remove from requirements.txt
May 14, 2024
9f59d85
Revert "add pygen to setup.py, remove from requirements.txt"
May 14, 2024
d673269
add pygen to unittests requirements.txt
May 14, 2024
9b21837
update unittests
May 14, 2024
e31a16f
hopefully fix multiapi
May 14, 2024
1ad22b4
keep it as just one venv in pygen
May 15, 2024
904ba52
typespec compiling with pygen inside dist
May 15, 2024
c033992
can copy pygen into dist when building and generate all typespec
May 15, 2024
ead7533
trying to remove packaging files from pygen
May 15, 2024
2bbe3ed
typespec and autorest.python working with vendored pygen
May 16, 2024
bdcd5a2
Merge branch 'main' of https://github.com/Azure/autorest.python into …
May 16, 2024
c1f8423
update chronus
May 16, 2024
93b88b2
fix version mismatch
May 16, 2024
04ada1a
lockfile
May 16, 2024
be564c3
add venv folder path
May 16, 2024
3f01476
fix template
May 16, 2024
3c699e7
add venv folder path with $ sign
May 16, 2024
975728a
don't put venv in the venvFolderPath
May 16, 2024
ef76337
add fi to venv script
May 16, 2024
866eae1
add tilde to semver
May 16, 2024
87d109d
rmeove venv from pipelines, too complicated
May 16, 2024
ab7a61f
udpate lockfile
May 16, 2024
1391c6f
revert dev_requirements.txt back to current in main
May 16, 2024
88784ea
remove pygen from reqs list
May 16, 2024
85bb831
add pygen to dev reqs
May 16, 2024
abab872
add pygen back to unittests reqs
May 16, 2024
287bb5c
Merge branch 'main' of https://github.com/Azure/autorest.python into …
May 28, 2024
823092f
temp
May 29, 2024
21c83fe
Merge branch 'main' of https://github.com/Azure/autorest.python into …
May 30, 2024
9abe97c
continue moving pygen to into typespec-python
May 30, 2024
ffbb90f
add dep on typespec-python in autorest.python
May 30, 2024
ecbd172
can generate autorest
May 30, 2024
3d341ab
can generate typespec locally
May 30, 2024
73b7570
update reqs
May 30, 2024
6578aa6
duplicate runpython3 script in autorest, fix dev reqs path
Jun 3, 2024
9bfc32c
Merge branch 'main' of https://github.com/Azure/autorest.python into …
Jun 3, 2024
b989020
move scripts for typespec out of pygen folder
Jun 3, 2024
f8c8a65
add scripts to incldued files
Jun 3, 2024
20b1c73
copyfiles venv into autorest.python
Jun 3, 2024
c5ee444
Merge branch 'main' of https://github.com/Azure/autorest.python into …
Jun 4, 2024
6757221
duplicate venv
Jun 4, 2024
40aec67
move venv into pygen folder
Jun 4, 2024
de79d1e
can generate typespec
Jun 4, 2024
856d0b2
fix installation for tsp and try to copy pygen into autorest.python
Jun 4, 2024
ee0722c
use own script for copying pygen
Jun 4, 2024
a3b5e26
fix readme and setup.py of autorest.pyton
Jun 4, 2024
96e1f56
copy pygen into autorest.python
Jun 4, 2024
a4fb4dc
try moving pygen up a folder
Jun 4, 2024
175d57d
make pygen a package again
Jun 6, 2024
2716cf3
keep trying with setup.py file
Jun 6, 2024
8fb27e1
remove generator from autorest.python committed code, can generate ty…
Jun 6, 2024
a6ff383
move venv into top level of package
Jun 6, 2024
1d757bb
get autorest.python running
Jun 6, 2024
4299812
Merge branch 'main' of https://github.com/Azure/autorest.python into …
Jun 7, 2024
36bfb77
add generator to dev reqs
Jun 7, 2024
3868253
fix venvtools path
Jun 7, 2024
47ed0b2
update dev reqs
Jun 7, 2024
ad8123b
add generator to published files
Jun 7, 2024
9624ee3
Merge branch 'main' of https://github.com/Azure/autorest.python into …
Jun 26, 2024
9f725b0
Merge branch 'main' into link_emitters
tadelesh Jul 2, 2024
9c3499e
update script
tadelesh Jul 3, 2024
75ef50f
update
tadelesh Jul 3, 2024
f38f275
Merge remote-tracking branch 'origin/main' into link_emitters
tadelesh Jul 3, 2024
9730829
update
tadelesh Jul 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .chronus/changes/link_emitters-2024-4-13-14-39-2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
changeKind: feature
packages:
- "@autorest/python"
- "@azure-tools/typespec-python"
---

add package pygen that both autorest.python and typespec-python will rely on
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,4 @@ node_modules/

# Generated test folders
test/services/*/_generated
**/autorest.python/generator
8 changes: 8 additions & 0 deletions eng/pipelines/ci-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,18 @@ steps:
displayName: Build project
workingDirectory: $(Build.SourcesDirectory)/autorest.python/

- script: pip list
displayName: List installed packages
workingDirectory: $(Build.SourcesDirectory)/autorest.python/packages/${{parameters.folderName}}

- script: pip install -r dev_requirements.txt
displayName: Pip install dev requirements
workingDirectory: $(Build.SourcesDirectory)/autorest.python/packages/${{parameters.folderName}}

- script: pip list
displayName: List installed packages
workingDirectory: $(Build.SourcesDirectory)/autorest.python/packages/${{parameters.folderName}}

- script: pylint ${{parameters.pythonFolderName}}
displayName: Pylint
workingDirectory: $(Build.SourcesDirectory)/autorest.python/packages/${{parameters.folderName}}
Expand Down
95 changes: 2 additions & 93 deletions packages/autorest.python/autorest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,59 +5,18 @@
# --------------------------------------------------------------------------
import logging
from pathlib import Path
import json
from abc import ABC, abstractmethod
from abc import abstractmethod
from typing import Any, Dict, Union, List

import yaml

from pygen import ReaderAndWriter, Plugin, YamlUpdatePlugin
from .jsonrpc import AutorestAPI
from ._version import VERSION


__version__ = VERSION
_LOGGER = logging.getLogger(__name__)


class ReaderAndWriter:
def __init__(self, *, output_folder: Union[str, Path], **kwargs: Any) -> None:
self.output_folder = Path(output_folder)
self._list_file: List[str] = []
try:
with open(
Path(self.output_folder) / Path("..") / Path("python.json"),
"r",
encoding="utf-8-sig",
) as fd:
python_json = json.load(fd)
except Exception: # pylint: disable=broad-except
python_json = {}
self.options = kwargs
if python_json:
_LOGGER.warning("Loading python.json file. This behavior will be depreacted")
self.options.update(python_json)

def read_file(self, path: Union[str, Path]) -> str:
"""Directly reading from disk"""
# make path relative to output folder
try:
with open(self.output_folder / Path(path), "r", encoding="utf-8-sig") as fd:
return fd.read()
except FileNotFoundError:
return ""

def write_file(self, filename: Union[str, Path], file_content: str) -> None:
"""Directly writing to disk"""
file_folder = Path(filename).parent
if not Path.is_dir(self.output_folder / file_folder):
Path.mkdir(self.output_folder / file_folder, parents=True)
with open(self.output_folder / Path(filename), "w", encoding="utf-8") as fd:
fd.write(file_content)

def list_file(self) -> List[str]:
return [str(f) for f in self.output_folder.glob("**/*") if f.is_file()]


class ReaderAndWriterAutorest(ReaderAndWriter):
def __init__(self, *, output_folder: Union[str, Path], autorestapi: AutorestAPI) -> None:
super().__init__(output_folder=output_folder)
Expand All @@ -73,23 +32,6 @@ def list_file(self) -> List[str]:
return self._autorestapi.list_inputs()


class Plugin(ReaderAndWriter, ABC):
"""A base class for autorest plugin.

:param autorestapi: An autorest API instance
"""

@abstractmethod
def process(self) -> bool:
"""The plugin process.

:rtype: bool
:returns: True if everything's ok, False optherwise
:raises Exception: Could raise any exception, stacktrace will be sent to autorest API
"""
raise NotImplementedError()


class PluginAutorest(Plugin, ReaderAndWriterAutorest):
"""For our Autorest plugins, we want to take autorest api as input as options, then pass it to the Plugin"""

Expand All @@ -102,39 +44,6 @@ def get_options(self) -> Dict[str, Any]:
"""Get the options bag using the AutorestAPI that we send to the parent plugin"""


class YamlUpdatePlugin(Plugin):
"""A plugin that update the YAML as input."""

def get_yaml(self) -> Dict[str, Any]:
# cadl file doesn't have to be relative to output folder
with open(self.options["cadl_file"], "r", encoding="utf-8-sig") as fd:
return yaml.safe_load(fd.read())

def write_yaml(self, yaml_string: str) -> None:
with open(self.options["cadl_file"], "w", encoding="utf-8-sig") as fd:
fd.write(yaml_string)

def process(self) -> bool:
# List the input file, should be only one
yaml_data = self.get_yaml()

self.update_yaml(yaml_data)

yaml_string = yaml.safe_dump(yaml_data)

self.write_yaml(yaml_string)
return True

@abstractmethod
def update_yaml(self, yaml_data: Dict[str, Any]) -> None:
"""The code-model-v4-no-tags yaml model tree.

:rtype: updated yaml
:raises Exception: Could raise any exception, stacktrace will be sent to autorest API
"""
raise NotImplementedError()


class YamlUpdatePluginAutorest(YamlUpdatePlugin, PluginAutorest): # pylint: disable=abstract-method
def get_yaml(self) -> Dict[str, Any]:
return yaml.safe_load(self.read_file("code-model-v4-no-tags.yaml"))
Expand Down
13 changes: 13 additions & 0 deletions packages/autorest.python/autorest/black.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
from typing import Dict, Any
from pygen.black import BlackScriptPlugin
from . import PluginAutorest


class BlackScriptPluginAutorest(BlackScriptPlugin, PluginAutorest):
def get_options(self) -> Dict[str, Any]:
return {"output_folder": self._autorestapi.get_value("outputFolderUri")}
117 changes: 117 additions & 0 deletions packages/autorest.python/autorest/codegen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
import logging
from typing import Dict, Any, Union
from pathlib import Path
import yaml

from pygen.codegen import CodeGenerator
from pygen.codegen.models import CodeModel
from pygen.codegen.serializers import JinjaSerializer

from . import ReaderAndWriterAutorest
from .jsonrpc import AutorestAPI
from . import PluginAutorest


_LOGGER = logging.getLogger(__name__)


class JinjaSerializerAutorest(JinjaSerializer, ReaderAndWriterAutorest):
def __init__(
self,
autorestapi: AutorestAPI,
code_model: CodeModel,
*,
output_folder: Union[str, Path],
**kwargs: Any,
) -> None:
super().__init__( # type: ignore
autorestapi=autorestapi,
code_model=code_model,
output_folder=output_folder,
**kwargs,
)


class CodeGeneratorAutorest(CodeGenerator, PluginAutorest):
def get_options(self) -> Dict[str, Any]:
if self._autorestapi.get_boolean_value("python3-only") is False:
_LOGGER.warning("You have passed in --python3-only=False. We have force overriden this to True.")
if self._autorestapi.get_boolean_value("add-python3-operation-files"):
_LOGGER.warning(
"You have passed in --add-python3-operation-files. "
"This flag no longer has an effect bc all SDKs are now Python3 only."
)
if self._autorestapi.get_boolean_value("reformat-next-link"):
_LOGGER.warning(
"You have passed in --reformat-next-link. We have force overriden "
"this to False because we no longer reformat initial query parameters into next "
"calls unless explicitly defined in the service definition."
)
options = {
"azure-arm": self._autorestapi.get_boolean_value("azure-arm"),
"header-text": self._autorestapi.get_value("header-text"),
"low-level-client": self._autorestapi.get_boolean_value("low-level-client", False),
"version-tolerant": self._autorestapi.get_boolean_value("version-tolerant", True),
"show-operations": self._autorestapi.get_boolean_value("show-operations"),
"python3-only": self._autorestapi.get_boolean_value("python3-only"),
"head-as-boolean": self._autorestapi.get_boolean_value("head-as-boolean", False),
"keep-version-file": self._autorestapi.get_boolean_value("keep-version-file"),
"no-async": self._autorestapi.get_boolean_value("no-async"),
"no-namespace-folders": self._autorestapi.get_boolean_value("no-namespace-folders"),
"basic-setup-py": self._autorestapi.get_boolean_value("basic-setup-py"),
"package-name": self._autorestapi.get_value("package-name"),
"package-version": self._autorestapi.get_value("package-version"),
"client-side-validation": self._autorestapi.get_boolean_value("client-side-validation"),
"tracing": self._autorestapi.get_boolean_value("trace"),
"multiapi": self._autorestapi.get_boolean_value("multiapi", False),
"polymorphic-examples": self._autorestapi.get_value("polymorphic-examples"),
"models-mode": self._autorestapi.get_value("models-mode"),
"builders-visibility": self._autorestapi.get_value("builders-visibility"),
"show-send-request": self._autorestapi.get_boolean_value("show-send-request"),
"only-path-and-body-params-positional": self._autorestapi.get_boolean_value(
"only-path-and-body-params-positional"
),
"combine-operation-files": self._autorestapi.get_boolean_value("combine-operation-files"),
"package-mode": self._autorestapi.get_value("package-mode"),
"package-pprint-name": self._autorestapi.get_value("package-pprint-name"),
"packaging-files-config": self._autorestapi.get_value("package-configuration"),
"default-optional-constants-to-none": self._autorestapi.get_boolean_value(
"default-optional-constants-to-none"
),
"generate-sample": self._autorestapi.get_boolean_value("generate-sample"),
"generate-test": self._autorestapi.get_boolean_value("generate-test"),
"default-api-version": self._autorestapi.get_value("default-api-version"),
}
return {k: v for k, v in options.items() if v is not None}

def get_yaml(self) -> Dict[str, Any]:
inputs = self._autorestapi.list_inputs()
_LOGGER.debug("Possible Inputs: %s", inputs)
if "code-model-v4-no-tags.yaml" not in inputs:
raise ValueError("code-model-v4-no-tags.yaml must be a possible input")

if self._autorestapi.get_value("input-yaml"):
input_yaml = self._autorestapi.get_value("input-yaml")
file_content = self._autorestapi.read_file(input_yaml)
else:
inputs = self._autorestapi.list_inputs()
_LOGGER.debug("Possible Inputs: %s", inputs)
if "code-model-v4-no-tags.yaml" not in inputs:
raise ValueError("code-model-v4-no-tags.yaml must be a possible input")

file_content = self._autorestapi.read_file("code-model-v4-no-tags.yaml")

# Parse the received YAML
return yaml.safe_load(file_content)

def get_serializer(self, code_model: CodeModel):
return JinjaSerializerAutorest(
self._autorestapi,
code_model,
output_folder=self.output_folder,
)
16 changes: 16 additions & 0 deletions packages/autorest.python/autorest/m2r.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
"""An autorest MD to RST plugin.
"""
from typing import Any, Dict

from pygen.m2r import M2R
from . import YamlUpdatePluginAutorest


class M2RAutorest(YamlUpdatePluginAutorest, M2R):
def get_options(self) -> Dict[str, Any]:
return {}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import logging
from typing import Callable, Dict, Any, Iterable, List, Optional, Set

from .._utils import (
from pygen.utils import (
to_snake_case,
KNOWN_TYPES,
get_body_type_for_description,
Expand Down
3 changes: 2 additions & 1 deletion packages/autorest.python/autorest/multiapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
from collections import defaultdict
from pathlib import Path
from typing import Dict, List, Optional, cast, Any
from pygen import Plugin, ReaderAndWriter

from .serializers import MultiAPISerializer, MultiAPISerializerAutorest
from .models import CodeModel
from .utils import _get_default_api_version_from_list

from .. import Plugin, PluginAutorest, ReaderAndWriter, ReaderAndWriterAutorest
from .. import PluginAutorest, ReaderAndWriterAutorest

_LOGGER = logging.getLogger(__name__)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
from pathlib import Path
from typing import Any, Optional, Union, List
from jinja2 import PackageLoader, Environment
from pygen import ReaderAndWriter
from pygen.utils import build_policies

from .import_serializer import FileImportSerializer

from ...jsonrpc import AutorestAPI
from ..models import CodeModel, GlobalParameter
from ... import ReaderAndWriter, ReaderAndWriterAutorest
from ..._utils import build_policies
from ... import ReaderAndWriterAutorest


__all__ = [
"MultiAPISerializer",
Expand Down Expand Up @@ -123,7 +125,7 @@ def serialize(self, code_model: CodeModel, no_async: Optional[bool]) -> None:

if not code_model.client.client_side_validation:
codegen_env = Environment(
loader=PackageLoader("autorest.codegen", "templates"),
loader=PackageLoader("pygen.codegen", "templates"),
keep_trailing_newline=True,
line_statement_prefix="##",
line_comment_prefix="###",
Expand Down
3 changes: 2 additions & 1 deletion packages/autorest.python/autorest/multiclient/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
from typing import Any, Dict
from pathlib import Path
from jinja2 import Environment, PackageLoader
from .. import Plugin, PluginAutorest
from pygen import Plugin
from .. import PluginAutorest

_LOGGER = logging.getLogger(__name__)

Expand Down
14 changes: 14 additions & 0 deletions packages/autorest.python/autorest/postprocess.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
from typing import Any, Dict

from pygen.postprocess import PostProcessPlugin
from . import PluginAutorest


class PostProcessPluginAutorest(PostProcessPlugin, PluginAutorest):
def get_options(self) -> Dict[str, Any]:
return {"outputFolderUri": self._autorestapi.get_value("outputFolderUri")}
Loading
Loading