-
Notifications
You must be signed in to change notification settings - Fork 8
feat: add jobs #314
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
Merged
Merged
feat: add jobs #314
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
bc2cfcb
feat: add jobs
tdstein 9c3d6dd
--wip-- [skip ci]
tdstein 9019386
refactor: introduce the active pattern
tdstein 4bfe3f8
add link to parent
tdstein a721b61
skip when Quarto unavailable
tdstein 107ee85
adds unit tests
tdstein a070f0a
adds docstrings
tdstein d196271
Update src/posit/connect/resources.py
tdstein d87cfe7
applies feedback discussed in pull requests
tdstein File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| from pathlib import Path | ||
|
|
||
| import pytest | ||
| from packaging import version | ||
|
|
||
| from posit import connect | ||
|
|
||
| from . import CONNECT_VERSION | ||
|
|
||
|
|
||
| class TestJobs: | ||
| @classmethod | ||
| def setup_class(cls): | ||
| cls.client = connect.Client() | ||
| cls.content = cls.client.content.create(name="example-quarto-minimal") | ||
|
|
||
| @classmethod | ||
| def teardown_class(cls): | ||
| cls.content.delete() | ||
| assert cls.client.content.count() == 0 | ||
|
|
||
| @pytest.mark.skipif( | ||
| CONNECT_VERSION <= version.parse("2023.01.1"), | ||
| reason="Quarto not available", | ||
| ) | ||
| def test(self): | ||
| content = self.content | ||
|
|
||
| path = Path("../../../resources/connect/bundles/example-quarto-minimal/bundle.tar.gz") | ||
| path = Path(__file__).parent / path | ||
| path = path.resolve() | ||
| path = str(path) | ||
|
|
||
| bundle = content.bundles.create(path) | ||
| bundle.deploy() | ||
|
|
||
| jobs = content.jobs | ||
| assert len(jobs) == 1 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,292 @@ | ||
| from typing import Literal, Optional, TypedDict, overload | ||
|
|
||
| from typing_extensions import NotRequired, Required, Unpack | ||
|
|
||
| from .resources import Active, ActiveFinderMethods, ActiveSequence, Resource | ||
|
|
||
| JobTag = Literal[ | ||
tdstein marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "unknown", | ||
| "build_report", | ||
| "build_site", | ||
| "build_jupyter", | ||
| "packrat_restore", | ||
| "python_restore", | ||
| "configure_report", | ||
| "run_app", | ||
| "run_api", | ||
| "run_tensorflow", | ||
| "run_python_api", | ||
| "run_dash_app", | ||
| "run_streamlit", | ||
| "run_bokeh_app", | ||
| "run_fastapi_app", | ||
| "run_pyshiny_app", | ||
| "render_shiny", | ||
| "run_voila_app", | ||
| "testing", | ||
| "git", | ||
| "val_py_ext_pkg", | ||
| "val_r_ext_pkg", | ||
| "val_r_install", | ||
| ] | ||
|
|
||
|
|
||
| class Job(Active): | ||
| class _Job(TypedDict): | ||
schloerke marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| # Identifiers | ||
| id: Required[str] | ||
tdstein marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| """A unique identifier for the job.""" | ||
|
|
||
| ppid: Required[Optional[str]] | ||
| """Identifier of the parent process.""" | ||
|
|
||
| pid: Required[str] | ||
| """Identifier of the process running the job.""" | ||
|
|
||
| key: Required[str] | ||
| """A unique key to identify this job.""" | ||
|
|
||
| remote_id: Required[Optional[str]] | ||
| """Identifier for off-host execution configurations.""" | ||
|
|
||
| app_id: Required[str] | ||
| """Identifier of the parent content associated with the job.""" | ||
|
|
||
| variant_id: Required[str] | ||
| """Identifier of the variant responsible for the job.""" | ||
|
|
||
| bundle_id: Required[str] | ||
| """Identifier of the content bundle linked to the job.""" | ||
|
|
||
| # Timestamps | ||
| start_time: Required[str] | ||
| """RFC3339 timestamp indicating when the job started.""" | ||
|
|
||
| end_time: Required[Optional[str]] | ||
| """RFC3339 timestamp indicating when the job finished.""" | ||
|
|
||
| last_heartbeat_time: Required[str] | ||
| """RFC3339 timestamp of the last recorded activity for the job.""" | ||
|
|
||
| queued_time: Required[Optional[str]] | ||
| """RFC3339 timestamp when the job was added to the queue.""" | ||
|
|
||
| # Status and Exit Information | ||
| status: Required[Literal[0, 1, 2]] | ||
| """Current status. Options are 0 (Active), 1 (Finished), and 2 (Finalized)""" | ||
|
|
||
| exit_code: Required[Optional[int]] | ||
| """The job's exit code, available after completion.""" | ||
|
|
||
| # Environment Information | ||
| hostname: Required[str] | ||
| """Name of the node processing the job.""" | ||
|
|
||
| cluster: Required[Optional[str]] | ||
| """Location where the job runs, either 'Local' or the cluster name.""" | ||
|
|
||
| image: Required[Optional[str]] | ||
| """Location of the content in clustered environments.""" | ||
|
|
||
| run_as: Required[str] | ||
| """UNIX user responsible for executing the job.""" | ||
|
|
||
| # Queue and Scheduling Information | ||
| queue_name: Required[Optional[str]] | ||
| """Name of the queue processing the job, relevant for scheduled reports.""" | ||
|
|
||
| # Job Metadata | ||
| tag: Required[JobTag] | ||
| """A tag categorizing the job type. Options are build_jupyter, build_report, build_site, configure_report, git, packrat_restore, python_restore, render_shiny, run_api, run_app, run_bokeh_app, run_dash_app, run_fastapi_app, run_pyshiny_app, run_python_api, run_streamlit, run_tensorflow, run_voila_app, testing, unknown, val_py_ext_pkg, val_r_ext_pkg, and val_r_install.""" | ||
|
|
||
| def __init__(self, ctx, parent: Active, **kwargs: Unpack[_Job]): | ||
| super().__init__(ctx, parent, **kwargs) | ||
| self._parent = parent | ||
|
|
||
| @property | ||
| def _endpoint(self) -> str: | ||
| return self._ctx.url + f"v1/content/{self._parent['guid']}/jobs/{self['key']}" | ||
tdstein marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| def destroy(self) -> None: | ||
| """Destroy the job. | ||
| Submit a request to kill the job. | ||
| Warnings | ||
| -------- | ||
| This operation is irreversible. | ||
| Note | ||
| ---- | ||
| This action requires administrator, owner, or collaborator privileges. | ||
| """ | ||
| self._ctx.session.delete(self._endpoint) | ||
|
|
||
|
|
||
| class Jobs( | ||
| ActiveFinderMethods[Job], | ||
| ActiveSequence[Job], | ||
| ): | ||
| def __init__(self, ctx, parent: Active, uid="key"): | ||
| """A collection of jobs. | ||
| Parameters | ||
| ---------- | ||
| ctx : Context | ||
| The context containing the HTTP session used to interact with the API. | ||
| parent : Active | ||
| Parent resource for maintaining hierarchical relationships | ||
| uid : str, optional | ||
| The default field name used to uniquely identify records, by default "key" | ||
| """ | ||
| super().__init__(ctx, parent, uid) | ||
| self._parent = parent | ||
|
|
||
| @property | ||
| def _endpoint(self) -> str: | ||
| return self._ctx.url + f"v1/content/{self._parent['guid']}/jobs" | ||
|
|
||
| def _create_instance(self, **kwargs) -> Job: | ||
| """Creates a `Job` instance. | ||
| Returns | ||
| ------- | ||
| Job | ||
| """ | ||
| return Job(self._ctx, self._parent, **kwargs) | ||
|
|
||
| class _FindByRequest(TypedDict, total=False): | ||
| # Identifiers | ||
| id: Required[str] | ||
| """A unique identifier for the job.""" | ||
|
|
||
| ppid: NotRequired[Optional[str]] | ||
| """Identifier of the parent process.""" | ||
|
|
||
| pid: NotRequired[str] | ||
| """Identifier of the process running the job.""" | ||
|
|
||
| key: NotRequired[str] | ||
| """A unique key to identify this job.""" | ||
|
|
||
| remote_id: NotRequired[Optional[str]] | ||
| """Identifier for off-host execution configurations.""" | ||
|
|
||
| app_id: NotRequired[str] | ||
| """Identifier of the parent content associated with the job.""" | ||
|
|
||
| variant_id: NotRequired[str] | ||
| """Identifier of the variant responsible for the job.""" | ||
|
|
||
| bundle_id: NotRequired[str] | ||
| """Identifier of the content bundle linked to the job.""" | ||
|
|
||
| # Timestamps | ||
| start_time: NotRequired[str] | ||
| """RFC3339 timestamp indicating when the job started.""" | ||
|
|
||
| end_time: NotRequired[Optional[str]] | ||
schloerke marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| """RFC3339 timestamp indicating when the job finished.""" | ||
|
|
||
| last_heartbeat_time: NotRequired[str] | ||
| """RFC3339 timestamp of the last recorded activity for the job.""" | ||
|
|
||
| queued_time: NotRequired[Optional[str]] | ||
| """RFC3339 timestamp when the job was added to the queue.""" | ||
|
|
||
| # Status and Exit Information | ||
| status: NotRequired[Literal[0, 1, 2]] | ||
| """Current status. Options are 0 (Active), 1 (Finished), and 2 (Finalized)""" | ||
|
|
||
| exit_code: NotRequired[Optional[int]] | ||
| """The job's exit code, available after completion.""" | ||
|
|
||
| # Environment Information | ||
| hostname: NotRequired[str] | ||
| """Name of the node processing the job.""" | ||
|
|
||
| cluster: NotRequired[Optional[str]] | ||
| """Location where the job runs, either 'Local' or the cluster name.""" | ||
|
|
||
| image: NotRequired[Optional[str]] | ||
| """Location of the content in clustered environments.""" | ||
|
|
||
| run_as: NotRequired[str] | ||
| """UNIX user responsible for executing the job.""" | ||
|
|
||
| # Queue and Scheduling Information | ||
| queue_name: NotRequired[Optional[str]] | ||
| """Name of the queue processing the job, relevant for scheduled reports.""" | ||
|
|
||
| # Job Metadata | ||
| tag: NotRequired[JobTag] | ||
| """A tag categorizing the job type. Options are build_jupyter, build_report, build_site, configure_report, git, packrat_restore, python_restore, render_shiny, run_api, run_app, run_bokeh_app, run_dash_app, run_fastapi_app, run_pyshiny_app, run_python_api, run_streamlit, run_tensorflow, run_voila_app, testing, unknown, val_py_ext_pkg, val_r_ext_pkg, and val_r_install.""" | ||
|
|
||
| @overload | ||
| def find_by(self, **conditions: Unpack[_FindByRequest]) -> Optional[Job]: | ||
| """Finds the first record matching the specified conditions. | ||
tdstein marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| There is no implied ordering so if order matters, you should specify it yourself. | ||
| Parameters | ||
| ---------- | ||
| id : str, not required | ||
| A unique identifier for the job. | ||
| ppid : Optional[str], not required | ||
| Identifier of the parent process. | ||
| pid : str, not required | ||
| Identifier of the process running the job. | ||
| key : str, not required | ||
| A unique key to identify this job. | ||
| remote_id : Optional[str], not required | ||
| Identifier for off-host execution configurations. | ||
| app_id : str, not required | ||
| Identifier of the parent content associated with the job. | ||
| variant_id : str, not required | ||
| Identifier of the variant responsible for the job. | ||
| bundle_id : str, not required | ||
| Identifier of the content bundle linked to the job. | ||
| start_time : str, not required | ||
| RFC3339 timestamp indicating when the job started. | ||
| end_time : Optional[str], not required | ||
| RFC3339 timestamp indicating when the job finished. | ||
| last_heartbeat_time : str, not required | ||
| RFC3339 timestamp of the last recorded activity for the job. | ||
| queued_time : Optional[str], not required | ||
| RFC3339 timestamp when the job was added to the queue. | ||
| status : int, not required | ||
| Current status. Options are 0 (Active), 1 (Finished), and 2 (Finalized) | ||
| exit_code : Optional[int], not required | ||
| The job's exit code, available after completion. | ||
| hostname : str, not required | ||
| Name of the node processing the job. | ||
| cluster : Optional[str], not required | ||
| Location where the job runs, either 'Local' or the cluster name. | ||
| image : Optional[str], not required | ||
| Location of the content in clustered environments. | ||
| run_as : str, not required | ||
| UNIX user responsible for executing the job. | ||
| queue_name : Optional[str], not required | ||
| Name of the queue processing the job, relevant for scheduled reports. | ||
| tag : JobTag, not required | ||
| A tag categorizing the job type. Options are build_jupyter, build_report, build_site, configure_report, git, packrat_restore, python_restore, render_shiny, run_api, run_app, run_bokeh_app, run_dash_app, run_fastapi_app, run_pyshiny_app, run_python_api, run_streamlit, run_tensorflow, run_voila_app, testing, unknown, val_py_ext_pkg, val_r_ext_pkg, and val_r_install. | ||
| Returns | ||
| ------- | ||
| Optional[Job] | ||
| """ | ||
| ... | ||
|
|
||
| @overload | ||
| def find_by(self, **conditions): ... | ||
|
|
||
| def find_by(self, **conditions) -> Optional[Job]: | ||
| return super().find_by(**conditions) | ||
|
|
||
|
|
||
| class JobsMixin(Active, Resource): | ||
| """Mixin class to add a jobs attribute to a resource.""" | ||
|
|
||
| def __init__(self, ctx, **kwargs): | ||
tdstein marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| super().__init__(ctx, **kwargs) | ||
| self.jobs = Jobs(ctx, self) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.