Skip to content

update samples to use environment configuration for client #233

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

THardy98
Copy link
Contributor

What was changed

Update samples to use environment configuration for clients.

Why?

Demonstrate environment configuration usage.

  1. Closes [Feature Request] Environment configuration #178

  2. How was this tested:
    Existing tests.

  3. Any docs updates needed?
    No

@THardy98 THardy98 requested a review from a team as a code owner August 12, 2025 02:59
@THardy98 THardy98 force-pushed the update_samples_env_config branch 2 times, most recently from 8e46a4e to a92c84f Compare August 12, 2025 04:14
@THardy98 THardy98 force-pushed the update_samples_env_config branch from a92c84f to df18612 Compare August 12, 2025 04:26
from temporalio.worker import Worker

from util import get_temporal_config_path
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One goal of samples was to never share code and let every sample be completely standalone. I don't think we want to violate this for a one-liner. Also, IMO we should be using the default shared path for all samples, not make a new config path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, IMO we should be using the default shared path for all samples, not make a new config path.

As in the default config path? I'm a bit hesitant to do that. What if the user already has a config file there? I also think it would be less immediately obvious than having the TOML file right in the repo.

The alternative if we want to keep every sample standalone is to add a TOML file for every sample.

Copy link
Member

@cretz cretz Aug 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in the default config path? I'm a bit hesitant to do that

Yes, it's how we did it in temporalio/samples-go#421 and I think samples need to show users how we expect them to use the API, which IMO is to use defaults unless there is a good reason not to. It makes sense for the sample to show the default/common/suggested/ideal/etc use of envconfig instead of encouraging them to make local config files.

What if the user already has a config file there?

Great, then the sample will work on that environment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, I forgot - we already talked about this:

config = ClientConfigProfile.load()
config["address"] = "localhost:7233"
Client.connect(**config.to_client_connect_config())

I'll update accordingly, my mistake

Copy link
Member

@cretz cretz Aug 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is annoying to have those three lines in every sample, I agree, but I can't think of a better way to achieve our goals of 1) no global/shared utilities across all samples, and 2) encourage use of the defaults as a user might in their code. Open to other suggestions here though or others' opinions in general.

Note, you may want this code:

config = ClientConfig.load_client_connect_config()
config.setdefault("target_host", "localhost:7233")
Client.connect(**config)

I think this is the more reasonable approach (load as normal, but set a default parameter value if it is not there). Many users may just remove the middle line when they don't want to have a default target host.

Copy link
Contributor Author

@THardy98 THardy98 Aug 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resulted in:

    config_dict = ClientConfigProfile.load().to_dict()
    config_dict.setdefault("address", "localhost:7233")
    config = ClientConfigProfile.from_dict(config_dict)
    cl = await client.Client.connect(**config.to_client_connect_config())

ClientConfigProfile is a frozen class, and we can't mutate from load_client_connect_config() because it expects the address to already be set (will error otherwise).

It's not the cleanest, but I'd rather this than make ClientConfigProfile mutable or remove the address assertion.

Copy link
Member

@cretz cretz Aug 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClientConfigProfile is a frozen class, and we can't mutate from load_client_connect_config()

ClientConfig.load_client_connect_config() doesn't return a ClientConfigProfile, it returns a dict. I think that simple 3-liner I had should work (create kwarg dict, set a key on it if not already set, use the kwarg dict).

Copy link
Contributor Author

@THardy98 THardy98 Aug 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but load_client_connect_config() calls to_client_connect_config

which has a check on address:

def to_client_connect_config(self) -> ClientConnectConfig:
        """Create a `ClientConnectConfig` from this profile."""
        if not self.address:
            raise ValueError(
                "Configuration profile must contain an 'address' to be used for "
                "client connection"
            )

which will error in the general case (no address is provided).
So I went the roundabout way with the to_dict -> from_dict

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 After off-PR discussion it was decided that this validation does not need to occur (Client.connect will error if it's missing the required param). We won't make some specific Python release for this minor change, but we may hold up this PR until a Python SDK release organically occurs with the change.

config_file=str(get_temporal_config_path())
)

client = await Client.connect(**config)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where on the Python side is the default target_host set for this load?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The address is set in the added temporal.toml file, is that what you're asking?

Copy link
Member

@cretz cretz Aug 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the other comment, if we are going with the default config file (which can be absent) to demonstrate ideal/suggested/simple/default/etc use of the envconfig API, then it may not have a target host here

config = ClientConfig.load_client_connect_config(
config_file=str(get_temporal_config_path())
)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pedantic, but probably don't need this blank line in every sample

@THardy98 THardy98 requested a review from cretz August 12, 2025 14:46
@THardy98 THardy98 force-pushed the update_samples_env_config branch from a3daf44 to 136e3fc Compare August 13, 2025 16:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature Request] Environment configuration
2 participants