@autorest/python 6.18.0 → 6.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/autorest/m4reformatter/__init__.py +15 -3
  2. package/generator/pygen/black.py +2 -3
  3. package/generator/pygen/codegen/models/__init__.py +2 -0
  4. package/generator/pygen/codegen/models/code_model.py +2 -4
  5. package/generator/pygen/codegen/models/combined_type.py +1 -1
  6. package/generator/pygen/codegen/models/credential_types.py +7 -14
  7. package/generator/pygen/codegen/models/enum_type.py +1 -1
  8. package/generator/pygen/codegen/models/lro_operation.py +0 -1
  9. package/generator/pygen/codegen/models/lro_paging_operation.py +1 -1
  10. package/generator/pygen/codegen/models/model_type.py +6 -9
  11. package/generator/pygen/codegen/models/operation.py +13 -16
  12. package/generator/pygen/codegen/models/paging_operation.py +0 -1
  13. package/generator/pygen/codegen/models/parameter_list.py +2 -5
  14. package/generator/pygen/codegen/models/primitive_types.py +35 -3
  15. package/generator/pygen/codegen/models/property.py +1 -9
  16. package/generator/pygen/codegen/serializers/__init__.py +1 -1
  17. package/generator/pygen/codegen/serializers/builder_serializer.py +12 -13
  18. package/generator/pygen/codegen/serializers/general_serializer.py +2 -2
  19. package/generator/pygen/codegen/serializers/model_serializer.py +2 -0
  20. package/generator/pygen/codegen/serializers/sample_serializer.py +20 -15
  21. package/generator/pygen/codegen/templates/model_base.py.jinja2 +30 -18
  22. package/generator/pygen/codegen/templates/vendor.py.jinja2 +0 -2
  23. package/generator/pygen/m2r.py +1 -1
  24. package/generator/pygen/postprocess/__init__.py +2 -2
  25. package/generator/pygen/postprocess/venvtools.py +1 -3
  26. package/generator/pygen/preprocess/__init__.py +1 -1
  27. package/generator/pygen/utils.py +1 -3
  28. package/generator/setup.py +1 -1
  29. package/package.json +11 -6
  30. package/scripts/__pycache__/venvtools.cpython-310.pyc +0 -0
  31. package/scripts/copy-generator.ts +24 -0
  32. package/scripts/eng/format.ts +5 -0
  33. package/scripts/eng/lint.ts +75 -0
  34. package/scripts/eng/mypy.ini +38 -0
  35. package/scripts/eng/pylintrc +58 -0
  36. package/scripts/eng/pyrightconfig.json +6 -0
  37. package/scripts/eng/regenerate.ts +298 -0
  38. package/scripts/eng/run-tests.ts +80 -0
  39. package/scripts/eng/utils.ts +38 -0
  40. package/scripts/mypy.ini +38 -0
  41. package/scripts/run-python3.ts +25 -0
  42. package/scripts/system-requirements.ts +253 -0
  43. package/scripts/copy-generator.js +0 -19
@@ -454,6 +454,10 @@ def _serialize(o, format: typing.Optional[str] = None): # pylint: disable=too-m
454
454
  return float(o)
455
455
  if isinstance(o, enum.Enum):
456
456
  return o.value
457
+ if isinstance(o, int):
458
+ if format == "str":
459
+ return str(o)
460
+ return o
457
461
  try:
458
462
  # First try datetime.datetime
459
463
  return _serialize_datetime(o, format)
@@ -489,6 +493,9 @@ def _create_value(rf: typing.Optional["_RestField"], value: typing.Any) -> typin
489
493
 
490
494
  class Model(_MyMutableMapping):
491
495
  _is_model = True
496
+ # label whether current class's _attr_to_rest_field has been calculated
497
+ # could not see _attr_to_rest_field directly because subclass inherits it from parent class
498
+ _calculated: typing.Set[str] = set()
492
499
 
493
500
  def __init__(self, *args: typing.Any, **kwargs: typing.Any) -> None:
494
501
  class_name = self.__class__.__name__
@@ -521,24 +528,27 @@ class Model(_MyMutableMapping):
521
528
  return Model(self.__dict__)
522
529
 
523
530
  def __new__(cls, *args: typing.Any, **kwargs: typing.Any) -> Self: # pylint: disable=unused-argument
524
- # we know the last three classes in mro are going to be 'Model', 'dict', and 'object'
525
- mros = cls.__mro__[:-3][::-1] # ignore model, dict, and object parents, and reverse the mro order
526
- attr_to_rest_field: typing.Dict[str, _RestField] = { # map attribute name to rest_field property
527
- k: v for mro_class in mros for k, v in mro_class.__dict__.items() if k[0] != "_" and hasattr(v, "_type")
528
- }
529
- annotations = {
530
- k: v
531
- for mro_class in mros
532
- if hasattr(mro_class, "__annotations__") # pylint: disable=no-member
533
- for k, v in mro_class.__annotations__.items() # pylint: disable=no-member
534
- }
535
- for attr, rf in attr_to_rest_field.items():
536
- rf._module = cls.__module__
537
- if not rf._type:
538
- rf._type = rf._get_deserialize_callable_from_annotation(annotations.get(attr, None))
539
- if not rf._rest_name_input:
540
- rf._rest_name_input = attr
541
- cls._attr_to_rest_field: typing.Dict[str, _RestField] = dict(attr_to_rest_field.items())
531
+ if f"{cls.__module__}.{cls.__qualname__}" not in cls._calculated:
532
+ # we know the last nine classes in mro are going to be 'Model', '_MyMutableMapping', 'MutableMapping',
533
+ # 'Mapping', 'Collection', 'Sized', 'Iterable', 'Container' and 'object'
534
+ mros = cls.__mro__[:-9][::-1] # ignore parents, and reverse the mro order
535
+ attr_to_rest_field: typing.Dict[str, _RestField] = { # map attribute name to rest_field property
536
+ k: v for mro_class in mros for k, v in mro_class.__dict__.items() if k[0] != "_" and hasattr(v, "_type")
537
+ }
538
+ annotations = {
539
+ k: v
540
+ for mro_class in mros
541
+ if hasattr(mro_class, "__annotations__") # pylint: disable=no-member
542
+ for k, v in mro_class.__annotations__.items() # pylint: disable=no-member
543
+ }
544
+ for attr, rf in attr_to_rest_field.items():
545
+ rf._module = cls.__module__
546
+ if not rf._type:
547
+ rf._type = rf._get_deserialize_callable_from_annotation(annotations.get(attr, None))
548
+ if not rf._rest_name_input:
549
+ rf._rest_name_input = attr
550
+ cls._attr_to_rest_field: typing.Dict[str, _RestField] = dict(attr_to_rest_field.items())
551
+ cls._calculated.add(f"{cls.__module__}.{cls.__qualname__}")
542
552
 
543
553
  return super().__new__(cls) # pylint: disable=no-value-for-parameter
544
554
 
@@ -669,6 +679,8 @@ def _get_deserialize_callable_from_annotation( # pylint: disable=R0911, R0915,
669
679
  rf: typing.Optional["_RestField"] = None,
670
680
  ) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]:
671
681
  if not annotation or annotation in [int, float]:
682
+ if annotation is int and rf and rf._format == "str":
683
+ return int
672
684
  return None
673
685
 
674
686
  # is it a type alias?
@@ -70,8 +70,6 @@ FileType = Union[
70
70
  Tuple[Optional[str], FileContent, Optional[str]],
71
71
  ]
72
72
 
73
- FilesType = Union[Mapping[str, FileType], Sequence[Tuple[str, FileType]]]
74
-
75
73
  def serialize_multipart_data_entry(data_entry: Any) -> Any:
76
74
  if isinstance(data_entry, (list, tuple, dict, Model)):
77
75
  return json.dumps(data_entry, cls=SdkJSONEncoder, exclude_readonly=True)
@@ -27,7 +27,7 @@ class GeneratorRenderer(m2r2.RestRenderer):
27
27
  return f":code:`{html}`"
28
28
 
29
29
 
30
- class M2R(YamlUpdatePlugin): # pylint: disable=abstract-method
30
+ class M2R(YamlUpdatePlugin):
31
31
  """A plugin to convert any description and summary from MD to RST."""
32
32
 
33
33
  def update_yaml(self, yaml_data: Dict[str, Any]) -> None:
@@ -3,7 +3,7 @@
3
3
  # Licensed under the MIT License. See License.txt in the project root for
4
4
  # license information.
5
5
  # --------------------------------------------------------------------------
6
- from typing import Tuple, Any, Dict
6
+ from typing import Tuple, Any
7
7
  from pathlib import Path
8
8
  import os
9
9
  import shutil
@@ -28,7 +28,7 @@ def format_file(file: Path, file_content: str) -> str:
28
28
  return file_content
29
29
 
30
30
 
31
- class PostProcessPlugin(Plugin): # pylint: disable=abstract-method
31
+ class PostProcessPlugin(Plugin):
32
32
  def __init__(self, **kwargs: Any):
33
33
  super().__init__(**kwargs)
34
34
  output_folder_uri = self.options["outputFolderUri"]
@@ -52,9 +52,7 @@ def create(
52
52
  return builder.context
53
53
 
54
54
 
55
- def python_run( # pylint: disable=inconsistent-return-statements
56
- venv_context, module, command, directory=_ROOT_DIR
57
- ) -> Optional[str]:
55
+ def python_run(venv_context, module, command, directory=_ROOT_DIR) -> Optional[str]:
58
56
  try:
59
57
  cmd_line = [
60
58
  venv_context.env_exe,
@@ -163,7 +163,7 @@ def has_multi_part_content_type(yaml_data: Dict[str, Any]) -> bool:
163
163
  return any(ct for ct in yaml_data.get("contentTypes", []) if ct == "multipart/form-data")
164
164
 
165
165
 
166
- class PreProcessPlugin(YamlUpdatePlugin): # pylint: disable=abstract-method
166
+ class PreProcessPlugin(YamlUpdatePlugin):
167
167
  """Add Python naming information."""
168
168
 
169
169
  @property
@@ -82,9 +82,7 @@ def parse_args(
82
82
  return value
83
83
 
84
84
  unknown_args_ret = {
85
- ua.strip("--").split("=", maxsplit=1)[0]: _get_value( # pylint: disable=bad-str-strip-call
86
- ua.strip("--").split("=", maxsplit=1)[1] # pylint: disable=bad-str-strip-call
87
- )
85
+ ua.strip("--").split("=", maxsplit=1)[0]: _get_value(ua.strip("--").split("=", maxsplit=1)[1])
88
86
  for ua in unknown_args
89
87
  }
90
88
  return args, unknown_args_ret
@@ -15,7 +15,7 @@ from setuptools import setup, find_packages
15
15
 
16
16
  # Version extraction inspired from 'requests'
17
17
  with open(os.path.join("pygen", "_version.py"), "r") as fd:
18
- version = re.search(r'^VERSION\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE).group(1)
18
+ version = re.search(r'^VERSION\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE).group(1) # type: ignore
19
19
 
20
20
  if not version:
21
21
  raise RuntimeError("Cannot find version information")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autorest/python",
3
- "version": "6.18.0",
3
+ "version": "6.20.0",
4
4
  "description": "The Python extension for generators in AutoRest.",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -20,12 +20,14 @@
20
20
  "homepage": "https://github.com/Azure/autorest.python/blob/main/README.md",
21
21
  "dependencies": {
22
22
  "@autorest/system-requirements": "~1.0.2",
23
- "fs-extra": "~11.2.0"
23
+ "fs-extra": "~11.2.0",
24
+ "tsx": "4.17.0"
24
25
  },
25
26
  "devDependencies": {
26
27
  "@microsoft.azure/autorest.testserver": "^3.3.46",
27
28
  "typescript": "~5.1.3",
28
- "@azure-tools/typespec-python": "^0.28.0"
29
+ "chalk": "5.3.0",
30
+ "@azure-tools/typespec-python": "^0.30.0"
29
31
  },
30
32
  "files": [
31
33
  "autorest/**/*.py",
@@ -37,8 +39,11 @@
37
39
  ],
38
40
  "scripts": {
39
41
  "start": "node ./scripts/run-python3.js ./scripts/start.py",
40
- "build": "node ./scripts/copy-generator.js --force",
41
- "install": "node ./scripts/copy-generator.js && node ./scripts/run-python3.js ./scripts/install.py",
42
- "debug": "node ./scripts/run-python3.js ./scripts/start.py --debug"
42
+ "build": "tsx ./scripts/copy-generator.ts --force",
43
+ "install": "tsx ./scripts/copy-generator.ts && node ./scripts/run-python3.js ./scripts/install.py",
44
+ "debug": "node ./scripts/run-python3.js ./scripts/start.py --debug",
45
+ "test": "tsx ./scripts/eng/run-tests.ts --validFolders azure/legacy azure/version-tolerant vanilla/legacy vanilla/version-tolerant dpg/version-tolerant",
46
+ "lint": "tsx ./scripts/eng/lint.ts --folderName autorest --skipEslint true",
47
+ "format": "tsx ./scripts/eng/format.ts"
43
48
  }
44
49
  }
@@ -0,0 +1,24 @@
1
+ import { copyFileSync, readdirSync } from "fs";
2
+ import { existsSync, removeSync, copySync } from "fs-extra";
3
+ import { join } from "path";
4
+
5
+ const force: boolean = process.argv[2] === "--force";
6
+
7
+ function copyAndCreateDir(sourceDir: string, destDir: string) {
8
+ // Delete the destination directory if it exists
9
+ if (existsSync(destDir)) {
10
+ if (force) removeSync(destDir);
11
+ else process.exit(0);
12
+ }
13
+
14
+ // Copy the source directory to the destination directory
15
+ copySync(sourceDir, destDir);
16
+ }
17
+
18
+ const typespecModulePath: string = join(__dirname, "..", "node_modules", "@azure-tools", "typespec-python");
19
+
20
+ // Copy the generator directory
21
+ copyAndCreateDir(join(typespecModulePath, "generator"), join(__dirname, "..", "generator"));
22
+
23
+ // Copy the scripts directory
24
+ copyAndCreateDir(join(typespecModulePath, "scripts", "eng"), join(__dirname, "..", "scripts", "eng"));
@@ -0,0 +1,5 @@
1
+ /* eslint-disable no-console */
2
+
3
+ import { runCommand } from "./utils.js";
4
+
5
+ runCommand("black .", "black");
@@ -0,0 +1,75 @@
1
+ /* eslint-disable no-console */
2
+ import yargs from "yargs";
3
+ import { hideBin } from "yargs/helpers";
4
+ import { runCommand, executeCommand } from "./utils.js";
5
+
6
+ interface Arguments {
7
+ folderName: string;
8
+ command?: "pylint" | "mypy" | "pyright" | "eslint";
9
+ skipWarning?: boolean;
10
+ skipEslint?: boolean;
11
+ }
12
+
13
+ const validCommands = ["pylint", "mypy", "pyright", "eslint"];
14
+
15
+ // PARSE INPUT ARGUMENTS
16
+ const argv = yargs(hideBin(process.argv))
17
+ .option("folderName", {
18
+ type: "string",
19
+ choices: ["generator", "autorest"],
20
+ description: "Specify the flavor",
21
+ default: "generator",
22
+ })
23
+ .option("command", {
24
+ alias: "c",
25
+ type: "string",
26
+ choices: validCommands,
27
+ description: "Specify the command to run",
28
+ })
29
+ .option("skipWarning", {
30
+ alias: "s",
31
+ type: "boolean",
32
+ description: "Skip to check warnings",
33
+ })
34
+ .option("skipEslint", {
35
+ alias: "e",
36
+ type: "boolean",
37
+ description: "Skip to check eslint",
38
+ }).argv as Arguments;
39
+
40
+ export function pylint() {
41
+ runCommand(`pylint ${argv.folderName}/ --rcfile ./scripts/eng/pylintrc`, "pylint");
42
+ }
43
+
44
+ export function mypy() {
45
+ runCommand(`mypy ${argv.folderName}/ --config-file ./scripts/eng/mypy.ini`, "mypy");
46
+ }
47
+
48
+ export function pyright() {
49
+ runCommand(`pyright ${argv.folderName}/ -p ./scripts/eng/pyrightconfig.json`, "pyright");
50
+ }
51
+
52
+ export function eslint() {
53
+ // const checkWarning = argv.skipWarning ? "" : "--max-warnings=0";
54
+ const checkWarning = "";
55
+ executeCommand(`npx eslint . --ext .ts ${checkWarning} `, "eslint");
56
+ }
57
+
58
+ if (argv.command === "pylint") {
59
+ pylint();
60
+ } else if (argv.command === "mypy") {
61
+ mypy();
62
+ } else if (argv.command === "pyright") {
63
+ pyright();
64
+ } else if (argv.command === "eslint") {
65
+ if (!argv.skipEslint) {
66
+ eslint();
67
+ }
68
+ } else {
69
+ pylint();
70
+ mypy();
71
+ pyright();
72
+ if (!argv.skipEslint) {
73
+ eslint();
74
+ }
75
+ }
@@ -0,0 +1,38 @@
1
+ # global configurations
2
+ [mypy]
3
+ python_version = 3.8
4
+
5
+
6
+ # module level configuratiohns
7
+ [mypy-jsonrpc.*]
8
+ ignore_missing_imports = True
9
+
10
+ [mypy-ptvsd.*]
11
+ ignore_missing_imports = True
12
+
13
+ [mypy-debugpy.*]
14
+ ignore_missing_imports = True
15
+
16
+ [mypy-m2r2.*]
17
+ ignore_missing_imports = True
18
+
19
+ [mypy-autorest.common.utils.*]
20
+ ignore_missing_imports = True
21
+
22
+ [mypy-autorest.common.python_mappings.*]
23
+ ignore_missing_imports = True
24
+
25
+ [mypy-pygen.codegen.models.*]
26
+ ignore_missing_imports = True
27
+
28
+ [mypy-setuptools]
29
+ ignore_missing_imports = True
30
+
31
+ [mypy-*._patch]
32
+ ignore_missing_imports = True
33
+
34
+ [mypy-pygen.*]
35
+ ignore_missing_imports = True
36
+
37
+ [mypy-yaml.*]
38
+ ignore_missing_imports = True
@@ -0,0 +1,58 @@
1
+ [MASTER]
2
+ py-version=3.8
3
+ ignore-patterns=test_*,conftest,setup
4
+ reports=no
5
+
6
+ # PYLINT DIRECTORY BLACKLIST.
7
+ ignore=_generated,samples,examples,test,tests,doc,.tox,generated_samples
8
+
9
+ [MESSAGES CONTROL]
10
+
11
+ # Add enable for useless disables
12
+ enable=useless-suppression
13
+
14
+ # For all codes, run 'pylint --list-msgs' or go to 'https://pylint.readthedocs.io/en/latest/reference_guide/features.html'
15
+ # locally-disabled: Warning locally suppressed using disable-msg
16
+ # cyclic-import: because of https://github.com/PyCQA/pylint/issues/850
17
+ # too-many-arguments: Due to the nature of the CLI many commands have large arguments set which reflect in large arguments set in corresponding methods.
18
+ # Let's black deal with bad-continuation
19
+ disable=useless-object-inheritance,missing-docstring,locally-disabled,fixme,cyclic-import,too-many-arguments,invalid-name,duplicate-code,too-few-public-methods,consider-using-f-string,super-with-arguments,redefined-builtin,import-outside-toplevel,client-suffix-needed,unnecessary-dunder-call,unnecessary-ellipsis,disallowed-name,consider-using-max-builtin
20
+
21
+ [FORMAT]
22
+ max-line-length=120
23
+
24
+ [VARIABLES]
25
+ # Tells whether we should check for unused import in __init__ files.
26
+ init-import=yes
27
+
28
+ [DESIGN]
29
+ # Maximum number of locals for function / method body
30
+ max-locals=25
31
+ # Maximum number of branch for function / method body
32
+ max-branches=20
33
+ # Maximum number of instance attributes for class
34
+ max-attributes=10
35
+ # Maximum number of ancestors
36
+ max-parents=15
37
+
38
+ [SIMILARITIES]
39
+ min-similarity-lines=10
40
+
41
+ [BASIC]
42
+ # Naming hints based on PEP 8 (https://www.python.org/dev/peps/pep-0008/#naming-conventions).
43
+ # Consider these guidelines and not hard rules. Read PEP 8 for more details.
44
+
45
+ # The invalid-name checker must be **enabled** for these hints to be used.
46
+ include-naming-hint=yes
47
+
48
+ # keep short; underscores are discouraged
49
+ module-naming-style=snake_case
50
+ const-naming-style=UPPER_CASE
51
+ class-naming-style=PascalCase
52
+ class-attribute-naming-style=snake_case
53
+ attr-naming-style=snake_case
54
+ method-naming-style=snake_case
55
+ function-naming-style=snake_case
56
+ argument-naming-style=snake_case
57
+ variable-naming-style=snake_case
58
+ inlinevar-naming-style=snake_case
@@ -0,0 +1,6 @@
1
+ {
2
+ "reportUnnecessaryCast": "warning",
3
+ "reportTypeCommentUsage": true,
4
+ "reportMissingImports": false,
5
+ "pythonVersion": "3.8"
6
+ }