microsoft / microsoft/typespec

[Bug]: [Python] Modeless ARM LRO callback references undefined `response`

Open Beginner friendly
#11,966 1 comment 1 reaction 0 assignees View on GitHub
bug emitter:client:python
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.