Skip to main content

Managing Projects

Retrieve one remote project

To inspect a project in Union, initialize Flyte and call the publicly re-exported Project class:

import flyte
from flyte.remote import Project

flyte.init(endpoint="dns:///your-union-endpoint")

project = Project.get("my_project")
print(project.to_dict())
print(project.to_json())

Project is a dataclass whose pb2 attribute contains the returned flyteidl.admin.project_pb2.Project message. It inherits ToJSONMixin, so to_dict() uses protobuf MessageToDict and to_json() uses protobuf MessageToJson for that payload.

Project.get(name, org=None) is implemented in remote/_project.py. It first calls ensure_client(), obtains get_client().project_domain_service, and sends a project_pb2.ProjectGetRequest with id=name to GetProject. The response protobuf is wrapped directly in a new Project instance. Although the method accepts org, the org=org request field is currently commented out; passing org therefore does not change the request sent by flyte-sdk.

The returned object can also be rendered through Rich. Its __rich_repr__() exposes these fields:

  • name and id from the protobuf message
  • description
  • the symbolic project state from Project.ProjectState.Name(...)
  • labels as a comma-separated key: value string, or None when the protobuf labels field is empty

For example, the CLI's named-project path uses the same method and Rich representation:

from rich.console import Console
from rich.pretty import pretty_repr

from flyte.remote import Project

console = Console()
console.print(pretty_repr(Project.get("my_project")))

The repository's pyflyte get project NAME command initializes its configured client with cfg.init(), calls Project.get(name), and prints pretty_repr(...).

List projects

Use Project.listall() when the goal is to enumerate projects rather than retrieve one by name:

import flyte
from flyte.remote import Project

flyte.init(endpoint="dns:///your-union-endpoint")

for project in Project.listall():
print(project.pb2.name, project.pb2.id)

listall accepts two optional arguments:

Project.listall(
filters=None,
sort_by=None,
)

Pass sorting as a (field, order) tuple. If omitted, flyte-sdk uses ("created_at", "asc"):

for project in Project.listall(sort_by=("created_at", "desc")):
print(project.to_dict())

The implementation converts the tuple to common_pb2.Sort. The exact string "asc" selects common_pb2.Sort.ASCENDING; any other order value follows the descending branch, so callers should use the documented "asc" or "desc" literals. A filters string, when supplied, is copied to the filters field of each project_pb2.ProjectListRequest:

for project in Project.listall(filters="your-project-filter"):
print(project.pb2.name)

The filter expression is passed through to the backend; Project does not parse or transform it.

Pagination behavior

Project.listall is backed by an async generator but is decorated with @syncify. Each request uses limit=100. After yielding every project in the response, it assigns resp.token to the next request's token. It continues making ListProjects calls until the service returns an empty token. Consequently, consuming the iterator can make multiple backend requests, and there is no caller-facing page-size or maximum-result argument.

Every yielded value is a Project wrapping one protobuf project, rather than the raw protobuf message. This makes the same to_dict(), to_json(), and Rich behavior available for list results as for Project.get() results.

Use the async form

Synchronous callers use the ordinary call shown above. In async code, use the .aio() form supplied by syncify:

import flyte
from flyte.remote import Project

await flyte.init.aio(endpoint="dns:///your-union-endpoint")

project = await Project.get.aio("my_project")
async for project in Project.listall.aio(sort_by=("created_at", "asc")):
print(project.pb2.name)

Project.get is an async implementation exposed synchronously by @syncify; its async counterpart returns an awaitable project. Project.listall is an async-generator implementation, so its async counterpart is consumed with async for. Async code should use .aio() rather than invoking the synchronous wrapper from the syncify background-loop thread; the syncify implementation detects that blocking situation and raises an assertion directing the caller to .aio().

How project requests reach Union

Both methods use the current ClientSet obtained through get_client():

  • Project.get(...) calls ClientSet.project_domain_service.GetProject(...).
  • Project.listall(...) calls ClientSet.project_domain_service.ListProjects(...).

ClientSet.project_domain_service returns the _admin_client as the ProjectDomainService client. The project methods construct the generated request messages from flyteidl.admin.project_pb2, and construct list ordering with flyteidl.admin.common_pb2.Sort.

Use the built-in CLI listing

The repository already wires both project operations into pyflyte get project:

@get.command()
@click.argument("name", type=str, required=False)
@click.pass_obj
def project(cfg: common.CLIConfig, name: str | None = None):
"""
Get a list of all projects, or details of a specific project by name.
"""
from flyte.remote import Project

cfg.init()

console = Console()
if name:
console.print(pretty_repr(Project.get(name)))
else:
console.print(common.format("Projects", Project.listall(), cfg.output_format))

With a name, the command displays one project using Rich. Without a name, it passes the Project.listall() iterator to common.format. The CLI output format is configured through CLIConfig; the declared formats are table, json, and table-simple.

Troubleshooting and current limitations

Client initialization errors

Call flyte.init(...) with a valid endpoint or API key before calling either project method. ensure_client() checks that a client exists, and get_client() raises InitializationError with a message instructing callers to initialize with an endpoint or API key when it does not. init also supports the configured authentication and transport options, but Project itself reads none of them directly.

Organization is not currently applied

Project.get exposes org, but the request construction contains # org=org, so the argument is ignored. Organization handling is also commented out in listall. The source contains a top-level TODO to add organization support again. Do not rely on org to scope either operation.

Sorting values

Use a two-item tuple such as ("created_at", "asc") or ("created_at", "desc"). The implementation treats only the exact string "asc" as ascending and routes every other value to descending; use the documented literals to avoid surprising ordering.

Large result sets

There is no limit parameter on Project.listall. The method requests 100 projects per RPC and follows continuation tokens internally, so iteration does not stop after the first 100 results. It stops only after a response has no continuation token.

Project state and labels in Rich output

Rich output converts the numeric protobuf state with Project.ProjectState.Name(...) and flattens labels into display text. The Rich representation is intended for presentation; use to_dict() or to_json() when you need the protobuf-backed serialized representation.