smithy-python 0.1.0__tar.gz

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.
@@ -0,0 +1,16 @@
1
+ {
2
+ "changes": [
3
+ {
4
+ "type": "feature",
5
+ "description": "Added the experimental smithy-python package and CLI scaffold."
6
+ },
7
+ {
8
+ "type": "feature",
9
+ "description": "Added service and data-shape selection for the native Python generator, including `--service` for `client` and `types` commands, service closure filtering, and standalone types name conflict diagnostics."
10
+ },
11
+ {
12
+ "type": "feature",
13
+ "description": "Added a Smithy JSON AST model loader with an embedded prelude, mixin flattening, and apply support."
14
+ }
15
+ ]
16
+ }
@@ -0,0 +1,36 @@
1
+ # Eclipse
2
+ .classpath
3
+ .project
4
+ .settings/
5
+
6
+ # Intellij
7
+ .idea/
8
+ *.iml
9
+ *.iws
10
+
11
+ # VS Code
12
+ .vscode/
13
+
14
+ # Mac
15
+ .DS_Store
16
+
17
+ # Maven
18
+ target/
19
+ **/dependency-reduced-pom.xml
20
+
21
+ # Gradle
22
+ **/.gradle
23
+ build/
24
+ */out/
25
+ **/*/out/
26
+
27
+ # Python
28
+ __pycache__
29
+ *.swp
30
+ .mypy_cache
31
+ .coverage
32
+ coverage.xml
33
+ htmlcov
34
+ *.egg-info
35
+ dist
36
+ .venv
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ ## v0.1.0
4
+
5
+ ### Features
6
+ * Added the experimental smithy-python package and CLI scaffold.
7
+ * Added service and data-shape selection for the native Python generator, including `--service` for `client` and `types` commands, service closure filtering, and standalone types name conflict diagnostics.
8
+ * Added a Smithy JSON AST model loader with an embedded prelude, mixin flattening, and apply support.
@@ -0,0 +1 @@
1
+ Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
@@ -0,0 +1,47 @@
1
+ Metadata-Version: 2.5
2
+ Name: smithy-python
3
+ Version: 0.1.0
4
+ Summary: A Smithy code generator for Python clients and types.
5
+ Project-URL: Changelog, https://github.com/smithy-lang/smithy-python/blob/develop/packages/smithy-python/CHANGELOG.md
6
+ Project-URL: Code, https://github.com/smithy-lang/smithy-python/tree/develop/packages/smithy-python/
7
+ Project-URL: Issue tracker, https://github.com/smithy-lang/smithy-python/issues
8
+ Author: Amazon Web Services
9
+ License: Apache License 2.0
10
+ License-File: NOTICE
11
+ Keywords: codegen,sdk,smithy
12
+ Classifier: Development Status :: 2 - Pre-Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: Apache Software License
15
+ Classifier: Natural Language :: English
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta
24
+ Classifier: Programming Language :: Python :: Implementation :: CPython
25
+ Classifier: Topic :: Software Development :: Code Generators
26
+ Requires-Python: >=3.12
27
+ Description-Content-Type: text/markdown
28
+
29
+ # smithy-python
30
+
31
+ > [!WARNING]
32
+ > This package is an experimental scaffold. It does not generate code yet. The
33
+ > Java generator in the repository's `codegen` directory remains authoritative.
34
+
35
+ `smithy-python` will provide Python-native code generation for Smithy models.
36
+ The initial command-line interface exposes the planned client and types generation
37
+ commands so that their top-level shape can be developed independently from the
38
+ generator implementation.
39
+
40
+ ```console
41
+ smithy-python generate client [OPTIONS]
42
+ smithy-python generate types [OPTIONS]
43
+ ```
44
+
45
+ After validating their invocation options, both generation commands currently exit
46
+ with an error explaining that generation has not been implemented. The package is
47
+ included in workspace builds to validate its packaging and entry points.
@@ -0,0 +1,19 @@
1
+ # smithy-python
2
+
3
+ > [!WARNING]
4
+ > This package is an experimental scaffold. It does not generate code yet. The
5
+ > Java generator in the repository's `codegen` directory remains authoritative.
6
+
7
+ `smithy-python` will provide Python-native code generation for Smithy models.
8
+ The initial command-line interface exposes the planned client and types generation
9
+ commands so that their top-level shape can be developed independently from the
10
+ generator implementation.
11
+
12
+ ```console
13
+ smithy-python generate client [OPTIONS]
14
+ smithy-python generate types [OPTIONS]
15
+ ```
16
+
17
+ After validating their invocation options, both generation commands currently exit
18
+ with an error explaining that generation has not been implemented. The package is
19
+ included in workspace builds to validate its packaging and entry points.
@@ -0,0 +1,51 @@
1
+ [project]
2
+ name = "smithy-python"
3
+ dynamic = ["version"]
4
+ requires-python = ">=3.12"
5
+ authors = [
6
+ {name = "Amazon Web Services"},
7
+ ]
8
+ description = "A Smithy code generator for Python clients and types."
9
+ readme = "README.md"
10
+ license = {text = "Apache License 2.0"}
11
+ keywords = ["smithy", "codegen", "sdk"]
12
+ classifiers = [
13
+ "Development Status :: 2 - Pre-Alpha",
14
+ "Intended Audience :: Developers",
15
+ "Natural Language :: English",
16
+ "License :: OSI Approved :: Apache Software License",
17
+ "Operating System :: OS Independent",
18
+ "Programming Language :: Python",
19
+ "Programming Language :: Python :: 3 :: Only",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Programming Language :: Python :: 3.14",
24
+ "Programming Language :: Python :: Implementation :: CPython",
25
+ "Programming Language :: Python :: Free Threading :: 2 - Beta",
26
+ "Topic :: Software Development :: Code Generators",
27
+ ]
28
+ dependencies = []
29
+
30
+ [project.scripts]
31
+ smithy-python = "smithy_python.cli:main"
32
+
33
+ [project.urls]
34
+ "Changelog" = "https://github.com/smithy-lang/smithy-python/blob/develop/packages/smithy-python/CHANGELOG.md"
35
+ "Code" = "https://github.com/smithy-lang/smithy-python/tree/develop/packages/smithy-python/"
36
+ "Issue tracker" = "https://github.com/smithy-lang/smithy-python/issues"
37
+
38
+ [build-system]
39
+ requires = ["hatchling"]
40
+ build-backend = "hatchling.build"
41
+
42
+ [tool.hatch.version]
43
+ path = "src/smithy_python/__init__.py"
44
+
45
+ [tool.hatch.build]
46
+ exclude = [
47
+ "tests",
48
+ ]
49
+
50
+ [tool.ruff]
51
+ src = ["src"]
@@ -0,0 +1,5 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """A Smithy code generator for Python clients and types."""
4
+
5
+ __version__ = "0.1.0"
@@ -0,0 +1,7 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ from .cli import main
5
+
6
+ if __name__ == "__main__":
7
+ raise SystemExit(main())
@@ -0,0 +1,180 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """Command-line interface for the Smithy Python code generator."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import argparse
8
+ import sys
9
+ from collections.abc import Mapping, Sequence
10
+ from dataclasses import dataclass
11
+ from pathlib import Path
12
+ from typing import BinaryIO, Final
13
+
14
+ from . import __version__
15
+ from .environment import PluginEnvironment
16
+ from .exceptions import CodegenError, InvalidInvocationError
17
+ from .model import load_model
18
+ from .selection import select_shapes
19
+
20
+ _GENERATION_NOT_IMPLEMENTED: Final = (
21
+ "smithy-python: error: {artifact} generation is not implemented yet\n"
22
+ )
23
+
24
+
25
+ @dataclass(frozen=True, slots=True)
26
+ class _Invocation:
27
+ artifact: str
28
+ model_source: bytes
29
+ output_dir: Path
30
+ environment: PluginEnvironment
31
+
32
+
33
+ def main(
34
+ argv: Sequence[str] | None = None,
35
+ *,
36
+ environ: Mapping[str, str] | None = None,
37
+ stdin: BinaryIO | None = None,
38
+ ) -> int:
39
+ """Run the CLI with the provided process inputs and return its exit code."""
40
+ parser = _create_parser()
41
+
42
+ try:
43
+ args = parser.parse_args(argv)
44
+ except SystemExit as error:
45
+ return error.code if isinstance(error.code, int) else 1
46
+
47
+ try:
48
+ invocation = _resolve_invocation(args, environ=environ, stdin=stdin)
49
+ except InvalidInvocationError as error:
50
+ sys.stderr.write(f"smithy-python: error: {error}\n")
51
+ return 2
52
+ except (CodegenError, OSError) as error:
53
+ sys.stderr.write(f"smithy-python: error: {error}\n")
54
+ return 1
55
+
56
+ try:
57
+ model = load_model(invocation.model_source)
58
+ selection = select_shapes(
59
+ model,
60
+ service_id=args.service,
61
+ require_service=invocation.artifact == "client",
62
+ )
63
+ except InvalidInvocationError as error:
64
+ sys.stderr.write(f"smithy-python: error: {error}\n")
65
+ return 2
66
+ except CodegenError as error:
67
+ sys.stderr.write(f"smithy-python: error: {error}\n")
68
+ return 1
69
+
70
+ if selection.excluded_count and selection.service is not None:
71
+ sys.stderr.write(
72
+ f"smithy-python: excluded {selection.excluded_count} eligible data shape(s) "
73
+ f"outside the closure of service {selection.service.id}\n"
74
+ )
75
+ sys.stderr.write(_GENERATION_NOT_IMPLEMENTED.format(artifact=args.artifact))
76
+ return 1
77
+
78
+
79
+ def _create_parser() -> argparse.ArgumentParser:
80
+ parser = argparse.ArgumentParser(
81
+ prog="smithy-python",
82
+ description="Generate Python source from Smithy models.",
83
+ )
84
+ parser.add_argument(
85
+ "--version",
86
+ action="version",
87
+ version=f"%(prog)s {__version__}",
88
+ )
89
+
90
+ commands = parser.add_subparsers(required=True)
91
+ generate = commands.add_parser("generate", help="Generate Python source")
92
+ artifacts = generate.add_subparsers(dest="artifact", required=True)
93
+ common = _common_artifact_options()
94
+ for name, help_text in (
95
+ ("client", "Generate a client package"),
96
+ ("types", "Generate a standalone types package"),
97
+ ):
98
+ artifacts.add_parser(name, help=help_text, parents=[common])
99
+
100
+ return parser
101
+
102
+
103
+ def _common_artifact_options() -> argparse.ArgumentParser:
104
+ """Build a parent parser with the options shared by every artifact."""
105
+ parser = argparse.ArgumentParser(add_help=False)
106
+ parser.add_argument(
107
+ "--service",
108
+ metavar="SHAPE_ID",
109
+ help="Absolute service shape ID; defaults to the sole non-mixin service.",
110
+ )
111
+ parser.add_argument(
112
+ "--model",
113
+ type=Path,
114
+ help=(
115
+ "The Smithy JSON AST model file to use for code generation. "
116
+ "If not set, the model is read from standard input."
117
+ ),
118
+ )
119
+ parser.add_argument(
120
+ "--output",
121
+ type=Path,
122
+ help=(
123
+ "Output directory for generated files. Defaults to the Smithy run "
124
+ "plugin's output directory (SMITHY_PLUGIN_DIR) when invoked by Smithy."
125
+ ),
126
+ )
127
+ return parser
128
+
129
+
130
+ def _resolve_invocation(
131
+ args: argparse.Namespace,
132
+ *,
133
+ environ: Mapping[str, str] | None,
134
+ stdin: BinaryIO | None,
135
+ ) -> _Invocation:
136
+ # PluginEnvironment falls back to os.environ, so the defaulting lives there.
137
+ environment = PluginEnvironment.from_environ(environ)
138
+ model_path: Path | None = args.model
139
+ output_path: Path | None = args.output
140
+
141
+ if (plugin_dir := environment.plugin_dir) is not None:
142
+ if model_path is not None:
143
+ raise InvalidInvocationError(
144
+ "--model cannot be used with the Smithy run plugin"
145
+ )
146
+ if output_path is not None:
147
+ raise InvalidInvocationError(
148
+ "--output cannot be used with the Smithy run plugin"
149
+ )
150
+ output_dir = plugin_dir
151
+ else:
152
+ if output_path is None:
153
+ raise InvalidInvocationError("Direct invocation requires --output")
154
+ output_dir = output_path
155
+
156
+ if model_path is not None:
157
+ # A model that cannot be read is an I/O failure like any other, whether
158
+ # the path is missing, a directory, or unreadable.
159
+ try:
160
+ model_source = model_path.read_bytes()
161
+ except OSError as error:
162
+ raise OSError(
163
+ f"Cannot read model {model_path}: {error.strerror or error}"
164
+ ) from error
165
+ else:
166
+ model_stream = sys.stdin.buffer if stdin is None else stdin
167
+ if environment.plugin_dir is None and model_stream.isatty():
168
+ raise InvalidInvocationError(
169
+ "Direct invocation requires --model or a model piped to standard input"
170
+ )
171
+ model_source = model_stream.read()
172
+ if not model_source:
173
+ raise InvalidInvocationError("Expected a Smithy JSON AST model")
174
+
175
+ return _Invocation(
176
+ artifact=args.artifact,
177
+ model_source=model_source,
178
+ output_dir=output_dir,
179
+ environment=environment,
180
+ )
@@ -0,0 +1,40 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """Smithy build environment provided to the code generator."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import os
8
+ from collections.abc import Mapping
9
+ from dataclasses import dataclass
10
+ from pathlib import Path
11
+ from typing import Self
12
+
13
+
14
+ @dataclass(frozen=True, slots=True)
15
+ class PluginEnvironment:
16
+ """Environment values supplied by Smithy's process-based run plugin."""
17
+
18
+ root_dir: Path | None = None
19
+ plugin_dir: Path | None = None
20
+ projection_name: str | None = None
21
+ artifact_name: str | None = None
22
+ includes_prelude: bool = False
23
+
24
+ @classmethod
25
+ def from_environ(cls, environ: Mapping[str, str] | None = None) -> Self:
26
+ """Load the Smithy run plugin environment from a mapping."""
27
+
28
+ source = os.environ if environ is None else environ
29
+
30
+ def path(name: str) -> Path | None:
31
+ return Path(value) if (value := source.get(name)) else None
32
+
33
+ return cls(
34
+ root_dir=path("SMITHY_ROOT_DIR"),
35
+ plugin_dir=path("SMITHY_PLUGIN_DIR"),
36
+ projection_name=source.get("SMITHY_PROJECTION_NAME"),
37
+ artifact_name=source.get("SMITHY_ARTIFACT_NAME"),
38
+ includes_prelude=source.get("SMITHY_INCLUDES_PRELUDE", "false").lower()
39
+ == "true",
40
+ )
@@ -0,0 +1,27 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """Exceptions raised by Smithy Python code generation."""
4
+
5
+
6
+ class SmithyPythonError(Exception):
7
+ """Base exception for errors raised by the Smithy Python generator."""
8
+
9
+
10
+ class CodegenError(SmithyPythonError):
11
+ """Raised when code generation fails."""
12
+
13
+
14
+ class InvalidInvocationError(SmithyPythonError):
15
+ """Raised when command-line inputs do not form a valid invocation."""
16
+
17
+
18
+ class ModelError(CodegenError):
19
+ """Raised when a Smithy model cannot be loaded or navigated."""
20
+
21
+
22
+ class InvalidShapeIdError(ModelError, ValueError):
23
+ """Raised when a string is not a valid Smithy shape ID."""
24
+
25
+
26
+ class ShapeNotFoundError(ModelError, LookupError):
27
+ """Raised when a shape ID does not resolve to a shape in the model."""
@@ -0,0 +1,56 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """The Smithy semantic model used by the Python code generator.
4
+
5
+ Use :func:`load_model` to parse a Smithy JSON AST into a :class:`Model`. The
6
+ loaded model is mixin-flattened, has ``apply`` entries merged, includes the
7
+ embedded Smithy prelude, and preserves the order of everything in the input.
8
+ """
9
+
10
+ from ..exceptions import InvalidShapeIdError, ModelError, ShapeNotFoundError
11
+ from ._loader import load_model
12
+ from ._model import Model
13
+ from ._shape_id import ShapeId
14
+ from ._shapes import (
15
+ EnumShape,
16
+ IntEnumShape,
17
+ JsonValue,
18
+ ListShape,
19
+ MapShape,
20
+ MemberShape,
21
+ Node,
22
+ OperationShape,
23
+ ResourceShape,
24
+ ServiceShape,
25
+ Shape,
26
+ ShapeType,
27
+ SimpleShape,
28
+ StructureShape,
29
+ UnionShape,
30
+ to_json,
31
+ )
32
+
33
+ __all__ = [
34
+ "EnumShape",
35
+ "IntEnumShape",
36
+ "InvalidShapeIdError",
37
+ "JsonValue",
38
+ "ListShape",
39
+ "MapShape",
40
+ "MemberShape",
41
+ "Model",
42
+ "ModelError",
43
+ "Node",
44
+ "OperationShape",
45
+ "ResourceShape",
46
+ "ServiceShape",
47
+ "Shape",
48
+ "ShapeId",
49
+ "ShapeNotFoundError",
50
+ "ShapeType",
51
+ "SimpleShape",
52
+ "StructureShape",
53
+ "UnionShape",
54
+ "load_model",
55
+ "to_json",
56
+ ]