microsoft / microsoft/typespec
[Bug]: [Python] Modeless ARM LRO callback references undefined `response`
- Dominant language
- Java
- Stars
- 5.9k
- Forks
- 394
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 104
Description
### Describe the bug
When generating a synchronous, modeless Python ARM client, an LRO operation whose final response has a JSON body but no response headers produces invalid Python code.
With `models-mode=none`, the generated `get_long_running_output` callback references `response` without assigning it:
```python
def get_long_running_output(pipeline_response):
if response.content:
deserialized = response.json()
else:
deserialized = None
```
When the long-running operation reaches a successful terminal state, the callback raises:
`NameError: name 'response' is not defined`
The callback should first assign the HTTP response:
```py
def get_long_running_output(pipeline_response):
response = pipeline_response.http_response
if response.content:
deserialized = response.json()
else:
deserialized = None
```
The current implementation in
packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py
only emits response = pipeline_response.http_response when one of these conditions is true:
- models are enabled;
- models-mode == "dpg" ; or
- the LRO final response contains response headers.
However, response_headers_and_deserialization(...) can still emit code that reads `response.content` when `models-mode=none` and the final response has no headers.
This is also a generated-output regression: an earlier TypeSpec-generated ARM client emitted the response assignment for the equivalent LRO callback.
### Reproduction
**Reproduction**:
````markdown
Reproduced with:
- Node.js 24.18.0
- `@typespec/compiler` 1.15.0
- `@azure-tools/typespec-python` 0.63.5
- `@typespec/http-client-python` 0.36.0
- `@azure-tools/typespec-azure-resource-manager` 0.71.0
- `@azure-tools/typespec-client-generator-core` 0.71.1
Create `main.tsp`:
```typespec
import "@typespec/rest";
import "@typespec/http";
import "@azure-tools/typespec-azure-resource-manager";
import "@typespec/versioning";
using Azure.ResourceManager;
using Versioning;
using Http;
@armProviderNamespace
@versioned(Versions)
namespace Microsoft.LroResponseRepro;
interface Operations extends Azure.ResourceManager.Operations {}
enum Versions {
@armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5)
@Azure.Core.previewVersion
`2025-01-01-preview`,
}
model WidgetProperties {
value: string;
...DefaultProvisioningStateProperty;
}
model Widget is TrackedResource {
...ResourceNameParameter;
}
@armResourceOperations
interface Widgets {
get is ArmResourceRead;
createOrUpdate is ArmResourceCreateOrReplaceAsync;
delete is ArmResourceDeleteWithoutOkAsync;
}
Compile it with:
tsp compile main.tsp \
--emit @azure-tools/typespec-python \
--option "@azure-tools/typespec-python.models-mode=none" \
--option "@azure-tools/typespec-python.no-async=true" \
--option "@azure-tools/typespec-python.namespace=lro_repro" \
--option "@azure-tools/typespec-python.emitter-output-dir=./generated"
Inspect generated/lro_repro/operations/_operations.py .
The generated begin_create_or_update callback reads response.content without first assigning
response = pipeline_response.http_response .
### Checklist
- [x] Follow our [Code of Conduct](https://github.com/microsoft/typespec/blob/main/CODE_OF_CONDUCT.md)
- [x] Check that there isn't already an issue that request the same bug to avoid creating a duplicate.
- [x] Check that this is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/Microsoft/typespec/discussions).
- [x] The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.
Contributor guide
Research direction
Start in packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py and trace response_headers_and_deserialization(...) for modeless ARM LRO callbacks. Reproduce with the supplied main.tsp compilation and inspect generated/lro_repro/operations/_operations.py. Done means the generated get_long_running_output callback assigns pipeline_response.http_response before reading response.content when the final response has no headers.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, python
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100