Skip to content

Commit

Permalink
feat: add command line interface
Browse files Browse the repository at this point in the history
  • Loading branch information
tdstein committed Sep 17, 2024
1 parent c4e761e commit 27a7c8f
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 68 deletions.
8 changes: 7 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ classifiers = [
"Typing :: Typed",
]
dynamic = ["version"]
dependencies = ["requests>=2.31.0,<3"]
dependencies = [
"click",
"requests>=2.31.0,<3",
]

[project.scripts]
posit = "posit:main"

[project.urls]
Source = "https://github.com/posit-dev/posit-sdk-py"
Expand Down
1 change: 1 addition & 0 deletions src/posit/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""The Posit SDK."""

from . import connect as connect
from .cli import main as main
40 changes: 40 additions & 0 deletions src/posit/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import io
import tarfile

import click

from . import connect


@click.group()
def main():
"""The Posit command line interface."""
pass


@main.group(name="connect")
def _connect():
"""Work with Posit Connect."""
pass


@_connect.command()
@click.argument("content")
@click.option("--output", "-o", default=".", help="Output directory for the downloaded content.")
def download(content, output):
"""Download content from Connect."""
client = connect.Client()
bundle = client.content.get(content).bundles.find_one()

if bundle is None:
return

buffer = io.BytesIO()
bundle.download(buffer)
buffer.seek(0)
with tarfile.open(fileobj=buffer, mode="r:gz") as tar:
tar.extractall(path=output, filter="data")


if __name__ == "__main__":
main()
21 changes: 7 additions & 14 deletions src/posit/connect/bundles.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from __future__ import annotations

import io
from typing import List
from typing import BinaryIO, List

from . import resources, tasks

Expand Down Expand Up @@ -46,7 +46,7 @@ def deploy(self) -> tasks.Task:
ts = tasks.Tasks(self.params)
return ts.get(result["task_id"])

def download(self, output: io.BufferedWriter | str) -> None:
def download(self, output: BinaryIO | str) -> None:
"""Download a bundle.
Download a bundle to a file or memory.
Expand All @@ -67,26 +67,19 @@ def download(self, output: io.BufferedWriter | str) -> None:
>>> bundle.download("bundle.tar.gz")
None
Write to an io.BufferedWriter.
Write to BinaryIO.
>>> with open('bundle.tar.gz', 'wb') as file:
>>> bundle.download(file)
None
"""
if not isinstance(output, (io.BufferedWriter, str)):
raise TypeError(
f"download() expected argument type 'io.BufferedWriter` or 'str', but got '{type(output).__name__}'"
)
if isinstance(output, str):
output = open(output, "wb")

path = f"v1/content/{self.content_guid}/bundles/{self.id}/download"
url = self.params.url + path
response = self.params.session.get(url, stream=True)
if isinstance(output, io.BufferedWriter):
for chunk in response.iter_content():
output.write(chunk)
elif isinstance(output, str):
with open(output, "wb") as file:
for chunk in response.iter_content():
file.write(chunk)
for chunk in response.iter_content():
output.write(chunk)


class Bundles(resources.Resources):
Expand Down
53 changes: 0 additions & 53 deletions tests/posit/connect/test_bundles.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import io
from unittest import mock

import pytest
import responses
from responses import matchers

Expand Down Expand Up @@ -226,40 +225,6 @@ def test_output_as_io(self):
assert mock_bundle_download.call_count == 1
assert file.read() == path.read_bytes()

@responses.activate
def test_invalid_arguments(self):
content_guid = "f2f37341-e21d-3d80-c698-a935ad614066"
bundle_id = "101"
path = get_path(f"v1/content/{content_guid}/bundles/{bundle_id}/download/bundle.tar.gz")

# behavior
mock_content_get = responses.get(
f"https://connect.example/__api__/v1/content/{content_guid}",
json=load_mock(f"v1/content/{content_guid}.json"),
)

mock_bundle_get = responses.get(
f"https://connect.example/__api__/v1/content/{content_guid}/bundles/{bundle_id}",
json=load_mock(f"v1/content/{content_guid}/bundles/{bundle_id}.json"),
)

mock_bundle_download = responses.get(
f"https://connect.example/__api__/v1/content/{content_guid}/bundles/{bundle_id}/download",
body=path.read_bytes(),
)

# setup
c = Client("https://connect.example", "12345")
bundle = c.content.get(content_guid).bundles.get(bundle_id)

# invoke
with pytest.raises(TypeError):
bundle.download(None)

# assert
assert mock_content_get.call_count == 1
assert mock_bundle_get.call_count == 1


class TestBundlesCreate:
@responses.activate
Expand Down Expand Up @@ -326,24 +291,6 @@ def test_kwargs_pathname(self):
assert mock_content_get.call_count == 1
assert mock_bundle_post.call_count == 1

@responses.activate
def test_invalid_arguments(self):
content_guid = "f2f37341-e21d-3d80-c698-a935ad614066"

# behavior
responses.get(
f"https://connect.example/__api__/v1/content/{content_guid}",
json=load_mock(f"v1/content/{content_guid}.json"),
)

# setup
c = Client("https://connect.example", "12345")
content = c.content.get(content_guid)

# invoke
with pytest.raises(TypeError):
content.bundles.create(None)


class TestBundlesFind:
@responses.activate
Expand Down

0 comments on commit 27a7c8f

Please sign in to comment.