diff --git a/pyproject.toml b/pyproject.toml index 9aa79a2aa..1dbb766e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ dev = [ "mypy-protobuf>=3.3.0,<4", "psutil>=5.9.3,<6", "pydocstyle>=6.3.0,<7", - "pydoctor>=24.11.1,<25", + "pydoctor>=25.10.1,<26", "pyright==1.1.403", "pytest~=7.4", "pytest-asyncio>=0.21,<0.22", @@ -158,6 +158,7 @@ intersphinx = [ "https://docs.python.org/3/objects.inv", "https://googleapis.dev/python/protobuf/latest/objects.inv", "https://opentelemetry-python.readthedocs.io/en/latest/objects.inv", + "https://nexus-rpc.github.io/sdk-python/objects.inv", ] privacy = [ "PRIVATE:temporalio.bridge", @@ -177,6 +178,7 @@ privacy = [ ] project-name = "Temporal Python" sidebar-expand-depth = 2 +warnings-as-errors = true [tool.pyright] enableTypeIgnoreComments = true diff --git a/temporalio/activity.py b/temporalio/activity.py index d726b9ef2..112abe4a8 100644 --- a/temporalio/activity.py +++ b/temporalio/activity.py @@ -288,7 +288,7 @@ def wait_sync(self, timeout: Optional[float] = None) -> None: def client() -> Client: """Return a Temporal Client for use in the current activity. - The client is only available in `async def` activities. + The client is only available in ``async def`` activities. In tests it is not available automatically, but you can pass a client when creating a :py:class:`temporalio.testing.ActivityEnvironment`. diff --git a/temporalio/common.py b/temporalio/common.py index 1975776cb..18432e867 100644 --- a/temporalio/common.py +++ b/temporalio/common.py @@ -1088,7 +1088,7 @@ def to_canonical_string(self) -> str: @staticmethod def from_canonical_string(canonical: str) -> WorkerDeploymentVersion: """Parse a version from a canonical string, which must be in the format - `.`. Deployment name must not have a `.` in it. + `.`. Deployment name must not have a ``.`` in it. """ parts = canonical.split(".", maxsplit=1) if len(parts) != 2: diff --git a/temporalio/contrib/openai_agents/workflow.py b/temporalio/contrib/openai_agents/workflow.py index 1023531a1..473b251b6 100644 --- a/temporalio/contrib/openai_agents/workflow.py +++ b/temporalio/contrib/openai_agents/workflow.py @@ -60,11 +60,13 @@ def activity_as_tool( of inputs and outputs between the agent and the activity. Note that if you take a context, mutation will not be persisted, as the activity may not be running in the same location. + For undocumented arguments, refer to :py:mod:`workflow` and :py:meth:`start_activity` + Args: fn: A Temporal activity function to convert to a tool. strict_json_schema: Whether the tool should follow a strict schema. See https://openai.github.io/openai-agents-python/ref/tool/#agents.tool.FunctionTool.strict_json_schema - For other arguments, refer to :py:mod:`workflow` :py:meth:`start_activity` + Returns: An OpenAI agent tool that wraps the provided activity. @@ -178,7 +180,7 @@ def nexus_operation_as_tool( of inputs and outputs between the agent and the operation. Args: - fn: A Nexus operation to convert into a tool. + operation: A Nexus operation to convert into a tool. service: The Nexus service class that contains the operation. endpoint: The Nexus endpoint to use for the operation. strict_json_schema: Whether the tool should follow a strict schema diff --git a/temporalio/envconfig.py b/temporalio/envconfig.py index 6f016dc75..3877e838c 100644 --- a/temporalio/envconfig.py +++ b/temporalio/envconfig.py @@ -184,7 +184,7 @@ class ClientConfigProfile: """Represents a client configuration profile. This class holds the configuration as loaded from a file or environment. - See `to_connect_config` to transform the profile to `ClientConnectConfig`, + See `to_client_connect_config` to transform the profile to `ClientConnectConfig`, which can be used to create a client. .. warning:: @@ -304,8 +304,8 @@ class ClientConfig: """Client configuration loaded from TOML and environment variables. This contains a mapping of profile names to client profiles. Use - `ClientConfigProfile.to_connect_config` to create a `ClientConnectConfig` - from a profile. See `load_profile` to load an individual profile. + `ClientConfigProfile.to_client_connect_config` to create a `ClientConnectConfig` + from a profile. See `ClientConfigProfile.load` to load an individual profile. .. warning:: Experimental API. diff --git a/temporalio/nexus/__init__.py b/temporalio/nexus/__init__.py index de9164716..8b24c0ada 100644 --- a/temporalio/nexus/__init__.py +++ b/temporalio/nexus/__init__.py @@ -6,15 +6,28 @@ See https://github.com/temporalio/sdk-python/tree/main#nexus """ -from ._decorators import workflow_run_operation as workflow_run_operation -from ._operation_context import Info as Info -from ._operation_context import LoggerAdapter as LoggerAdapter -from ._operation_context import NexusCallback as NexusCallback +from ._decorators import workflow_run_operation from ._operation_context import ( - WorkflowRunOperationContext as WorkflowRunOperationContext, + Info, + LoggerAdapter, + NexusCallback, + WorkflowRunOperationContext, + client, + in_operation, + info, + logger, +) +from ._token import WorkflowHandle + +__all__ = ( + "workflow_run_operation", + "Info", + "LoggerAdapter", + "NexusCallback", + "WorkflowRunOperationContext", + "client", + "in_operation", + "info", + "logger", + "WorkflowHandle", ) -from ._operation_context import client as client -from ._operation_context import in_operation as in_operation -from ._operation_context import info as info -from ._operation_context import logger as logger -from ._token import WorkflowHandle as WorkflowHandle diff --git a/temporalio/nexus/_operation_context.py b/temporalio/nexus/_operation_context.py index 098bba8a1..42a9448a5 100644 --- a/temporalio/nexus/_operation_context.py +++ b/temporalio/nexus/_operation_context.py @@ -23,8 +23,6 @@ import temporalio.api.common.v1 import temporalio.api.workflowservice.v1 import temporalio.common -from temporalio.nexus import _link_conversion -from temporalio.nexus._token import WorkflowHandle from temporalio.types import ( MethodAsyncNoParam, MethodAsyncSingleParam, @@ -34,6 +32,13 @@ SelfType, ) +from ._link_conversion import ( + nexus_link_to_workflow_event, + workflow_event_to_nexus_link, + workflow_execution_started_event_link_from_workflow_handle, +) +from ._token import WorkflowHandle + if TYPE_CHECKING: import temporalio.client @@ -162,7 +167,7 @@ def _get_workflow_event_links( ) -> list[temporalio.api.common.v1.Link.WorkflowEvent]: event_links = [] for inbound_link in self.nexus_context.inbound_links: - if link := _link_conversion.nexus_link_to_workflow_event(inbound_link): + if link := nexus_link_to_workflow_event(inbound_link): event_links.append(link) return event_links @@ -182,14 +187,13 @@ def _add_outbound_links( wf_event_links.append(link.workflow_event) if not wf_event_links: wf_event_links = [ - _link_conversion.workflow_execution_started_event_link_from_workflow_handle( + workflow_execution_started_event_link_from_workflow_handle( workflow_handle, self.nexus_context.request_id, ) ] self.nexus_context.outbound_links.extend( - _link_conversion.workflow_event_to_nexus_link(link) - for link in wf_event_links + workflow_event_to_nexus_link(link) for link in wf_event_links ) except Exception as e: logger.warning( diff --git a/temporalio/nexus/_operation_handlers.py b/temporalio/nexus/_operation_handlers.py index aa5351353..2b128ab2f 100644 --- a/temporalio/nexus/_operation_handlers.py +++ b/temporalio/nexus/_operation_handlers.py @@ -91,7 +91,7 @@ async def _cancel_workflow( Args: token: The token of the workflow to cancel. kwargs: Additional keyword arguments - to pass to the workflow cancel method. + to pass to the workflow cancel method. """ try: nexus_workflow_handle = WorkflowHandle[Any].from_token(token) diff --git a/temporalio/nexus/_util.py b/temporalio/nexus/_util.py index de5bf94b2..46a5b8aa0 100644 --- a/temporalio/nexus/_util.py +++ b/temporalio/nexus/_util.py @@ -39,7 +39,7 @@ def get_workflow_run_start_method_input_and_output_type_annotations( ]: """Return operation input and output types. - `start` must be a type-annotated start method that returns a + ``start`` must be a type-annotated start method that returns a :py:class:`temporalio.nexus.WorkflowHandle`. """ input_type, output_type = _get_start_method_input_and_output_type_annotations(start) @@ -125,7 +125,7 @@ def get_operation_factory( Optional[Callable[[Any], Any]], Optional[nexusrpc.Operation[Any, Any]], ]: - """Return the :py:class:`Operation` for the object along with the factory function. + """Return the :py:class:`nexusrpc.Operation` for the object along with the factory function. ``obj`` should be a decorated operation start method. """ @@ -145,7 +145,7 @@ def set_operation_factory( obj: Any, operation_factory: Callable[[Any], Any], ) -> None: - """Set the :py:class:`OperationHandler` factory for this object. + """Set the :py:class:`nexusrpc.handler.OperationHandler` factory for this object. ``obj`` should be an operation start method. """ @@ -158,7 +158,7 @@ def set_operation_factory( # # This file is licensed under the MIT License. def is_async_callable(obj: Any) -> bool: - """Return True if `obj` is an async callable. + """Return True if ``obj`` is an async callable. Supports partials of async callable class instances. """ diff --git a/temporalio/runtime.py b/temporalio/runtime.py index c3546c900..d3dd544df 100644 --- a/temporalio/runtime.py +++ b/temporalio/runtime.py @@ -69,7 +69,7 @@ class Runtime: thread pool is created. Runtimes do not work across forks. Advanced users should consider using - :py:meth:`prevent_default` and `:py:meth`set_default` to ensure each + :py:meth:`prevent_default` and :py:meth:`set_default` to ensure each fork creates it's own runtime. """ diff --git a/temporalio/service.py b/temporalio/service.py index 349bce763..988a62927 100644 --- a/temporalio/service.py +++ b/temporalio/service.py @@ -230,11 +230,8 @@ async def check_health( metadata: Mapping[str, Union[str, bytes]] = {}, timeout: Optional[timedelta] = None, ) -> bool: - """Check whether the WorkflowService is up. - - In addition to accepting which service to check health on, this accepts - some of the same parameters as other RPC calls. See - :py:meth:`ServiceCall.__call__`. + """Check whether the provided service is up. If no service is specified, + the WorkflowService is used. Returns: True when available, false if the server is running but the service diff --git a/temporalio/worker/_interceptor.py b/temporalio/worker/_interceptor.py index a60ebb1d5..e430aee9e 100644 --- a/temporalio/worker/_interceptor.py +++ b/temporalio/worker/_interceptor.py @@ -469,5 +469,5 @@ def start_local_activity( async def start_nexus_operation( self, input: StartNexusOperationInput[InputT, OutputT] ) -> temporalio.workflow.NexusOperationHandle[OutputT]: - """Called for every :py:func:`temporalio.workflow.start_nexus_operation` call.""" + """Called for every :py:func:`temporalio.workflow.NexusClient.start_operation` call.""" return await self.next.start_nexus_operation(input) diff --git a/temporalio/worker/_tuning.py b/temporalio/worker/_tuning.py index 84abd87ac..909aca706 100644 --- a/temporalio/worker/_tuning.py +++ b/temporalio/worker/_tuning.py @@ -27,7 +27,7 @@ class FixedSizeSlotSupplier: @dataclass(frozen=True) class ResourceBasedTunerConfig: - """Options for a :py:class:`ResourceBasedTuner` or a :py:class:`ResourceBasedSlotSupplier`.""" + """Options for a :py:class:`ResourceBasedSlotSupplier`.""" target_memory_usage: float """A value between 0 and 1 that represents the target (system) memory usage. It's not recommended @@ -62,7 +62,7 @@ class ResourceBasedSlotSupplier: slot_config: ResourceBasedSlotConfig tuner_config: ResourceBasedTunerConfig """Options for the tuner that will be used to adjust the number of slots. When used with a - :py:class:`CompositeTuner`, all resource-based slot suppliers must use the same tuner options.""" + :py:class:`_CompositeTuner`, all resource-based slot suppliers must use the same tuner options.""" class SlotPermit: diff --git a/temporalio/worker/_worker.py b/temporalio/worker/_worker.py index 5053c3190..fabf66d43 100644 --- a/temporalio/worker/_worker.py +++ b/temporalio/worker/_worker.py @@ -162,7 +162,7 @@ def __init__( :py:func:`@activity.defn`. Activities may be async functions or non-async functions. nexus_service_handlers: Instances of Nexus service handler classes - decorated with :py:func:`@nexusrpc.handler.service_handler`. + decorated with :py:func:`@nexusrpc.handler.service_handler`. .. warning:: This parameter is experimental and unstable. @@ -187,7 +187,7 @@ def __init__( the worker is shut down. nexus_task_executor: Executor to use for non-async Nexus operations. This is required if any operation start methods - are non-`async def`. :py:class:`concurrent.futures.ThreadPoolExecutor` + are non-``async def``. :py:class:`concurrent.futures.ThreadPoolExecutor` is recommended. .. warning:: @@ -298,14 +298,14 @@ def __init__( on_fatal_error: An async function that can handle a failure before the worker shutdown commences. This cannot stop the shutdown and any exception raised is logged and ignored. - use_worker_versioning: If true, the `build_id` argument must be + use_worker_versioning: If true, the ``build_id`` argument must be specified, and this worker opts into the worker versioning feature. This ensures it only receives workflow tasks for workflows which it claims to be compatible with. For more information, see https://docs.temporal.io/workers#worker-versioning. - Exclusive with `deployment_config`. - WARNING: Deprecated. Use `deployment_config` instead. + Exclusive with ``deployment_config``. + WARNING: Deprecated. Use ``deployment_config`` instead. disable_safe_workflow_eviction: If true, instead of letting the workflow collect its tasks properly, the worker will simply let the Python garbage collector collect the tasks. WARNING: Users @@ -313,8 +313,8 @@ def __init__( throw ``GeneratorExit`` in coroutines causing them to wake up in different threads and run ``finally`` and other code in the wrong workflow environment. - deployment_config: Deployment config for the worker. Exclusive with `build_id` and - `use_worker_versioning`. + deployment_config: Deployment config for the worker. Exclusive with ``build_id`` and + ``use_worker_versioning``. WARNING: This is an experimental feature and may change in the future. workflow_task_poller_behavior: Specify the behavior of workflow task polling. Defaults to a 5-poller maximum. diff --git a/temporalio/worker/_workflow.py b/temporalio/worker/_workflow.py index 6e7c254aa..be7ebbf09 100644 --- a/temporalio/worker/_workflow.py +++ b/temporalio/worker/_workflow.py @@ -626,7 +626,7 @@ class _DeadlockError(Exception): """Exception class for deadlocks. Contains functionality to swap the default traceback for another.""" def __init__(self, message: str, replacement_tb: Optional[TracebackType] = None): - """Create a new DeadlockError, with message `message` and optionally a traceback `replacement_tb` to be swapped in later. + """Create a new DeadlockError, with message ``message`` and optionally a traceback ``replacement_tb`` to be swapped in later. Args: message: Message to be presented through exception. diff --git a/temporalio/worker/workflow_sandbox/_importer.py b/temporalio/worker/workflow_sandbox/_importer.py index 7b5263340..d8ab9ed6d 100644 --- a/temporalio/worker/workflow_sandbox/_importer.py +++ b/temporalio/worker/workflow_sandbox/_importer.py @@ -339,7 +339,6 @@ def _maybe_passthrough_module(self, name: str) -> Optional[types.ModuleType]: def _maybe_restrict_module( self, mod: types.ModuleType ) -> Optional[types.ModuleType]: - """Implements :py:meth:`_Environment.maybe_restrict_module`.""" matcher = self.restrictions.invalid_module_members.child_matcher( *mod.__name__.split(".") ) diff --git a/temporalio/worker/workflow_sandbox/_restrictions.py b/temporalio/worker/workflow_sandbox/_restrictions.py index e9b6f09e4..75ad86577 100644 --- a/temporalio/worker/workflow_sandbox/_restrictions.py +++ b/temporalio/worker/workflow_sandbox/_restrictions.py @@ -85,7 +85,7 @@ def default_message(qualified_name: str) -> str: class UnintentionalPassthroughError(temporalio.exceptions.TemporalError): """Error that occurs when a workflow unintentionally passes an import to the sandbox when - the import notification policy includes :py:attr:`temporalio.workflow.SandboxImportNotificationPolicy.RAISE_ON_NON_PASSTHROUGH`. + the import notification policy includes :py:attr:`temporalio.workflow.SandboxImportNotificationPolicy.RAISE_ON_UNINTENTIONAL_PASSTHROUGH`. Attributes: qualified_name: Fully qualified name of what was passed through to the sandbox. @@ -196,7 +196,7 @@ def with_passthrough_all_modules(self) -> SandboxRestrictions: def with_import_notification_policy( self, policy: temporalio.workflow.SandboxImportNotificationPolicy ) -> SandboxRestrictions: - """Create a new restriction set with the given import notification policy as the :py:attr:`import_policy`.""" + """Create a new restriction set with the given import notification policy as the :py:attr:`import_notification_policy`.""" return dataclasses.replace(self, import_notification_policy=policy) diff --git a/temporalio/workflow.py b/temporalio/workflow.py index b3b429cdb..4d9340436 100644 --- a/temporalio/workflow.py +++ b/temporalio/workflow.py @@ -4355,7 +4355,7 @@ async def start_child_workflow( static_details: General fixed details for this child workflow execution that may appear in UI/CLI. This can be in Temporal markdown format and can span multiple lines. This is a fixed value on the workflow that cannot be updated. For details that can be - updated, use :py:meth:`Workflow.get_current_details` within the workflow. + updated, use :py:meth:`get_current_details` within the workflow. priority: Priority to use for this workflow. Returns: @@ -5300,7 +5300,7 @@ class NexusOperationCancellationType(IntEnum): Pass one of these values to :py:meth:`NexusClient.start_operation` to define cancellation behavior. - To initiate cancellation, use :py:meth:`NexusOperationHandle.cancel` and then `await` the + To initiate cancellation, use :py:meth:`NexusOperationHandle.cancel` and then ``await`` the operation handle. This will result in a :py:class:`exceptions.NexusOperationError`. The values of this enum define what is guaranteed to have happened by that point. """ diff --git a/uv.lock b/uv.lock index 6ac333e4b..7d8113d22 100644 --- a/uv.lock +++ b/uv.lock @@ -2297,7 +2297,7 @@ wheels = [ [[package]] name = "pydoctor" -version = "24.11.2" +version = "25.10.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -2307,13 +2307,13 @@ dependencies = [ { name = "lunr" }, { name = "platformdirs" }, { name = "requests" }, - { name = "toml", marker = "python_full_version < '3.11'" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "twisted" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/7c/c3491dd1666b9fdede4bddb2ebf9a1bc31ff8e0536ee6572ad5a028e109b/pydoctor-24.11.2.tar.gz", hash = "sha256:d52c13caa17b870da1a245981c15cda88406eb73a863dbe23db3fbc43e3b3fc3", size = 946366, upload-time = "2025-01-08T20:04:41.584Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/26/4b49eab9203a0f6939711f6a62a06c04d6e11fbf01e7b2cd9f9dece97686/pydoctor-25.10.1.tar.gz", hash = "sha256:489ec8b96f1e477df8f1892e2c7990836f32481a633ed13abb5e24a3488c83fb", size = 981473, upload-time = "2025-09-29T22:06:49.712Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/c2/016a296de5eb70478c65cc58722f11aedd858bf248431486b0dff479016c/pydoctor-24.11.2-py3-none-any.whl", hash = "sha256:69004e7b4a2b4db6425f1a46869adf7ebcd7c4fd0340515538134a4df181ab82", size = 1584835, upload-time = "2025-01-08T20:04:39.394Z" }, + { url = "https://files.pythonhosted.org/packages/2e/ae/1ae23968390bfd71678c1cd752e66a9823e2fe45ba34a6740f76e84011ab/pydoctor-25.10.1-py3-none-any.whl", hash = "sha256:2aa85f8d64e11c065d71a2317b82724a58361173d945290509367681665bdc7c", size = 1637603, upload-time = "2025-09-29T22:06:47.349Z" }, ] [[package]] @@ -3046,7 +3046,7 @@ dev = [ { name = "openai-agents", extras = ["litellm"], marker = "python_full_version < '3.14'", specifier = ">=0.3,<0.4" }, { name = "psutil", specifier = ">=5.9.3,<6" }, { name = "pydocstyle", specifier = ">=6.3.0,<7" }, - { name = "pydoctor", specifier = ">=24.11.1,<25" }, + { name = "pydoctor", specifier = ">=25.10.1,<26" }, { name = "pyright", specifier = "==1.1.403" }, { name = "pytest", specifier = "~=7.4" }, { name = "pytest-asyncio", specifier = ">=0.21,<0.22" },