Skip to content

Commit

Permalink
#29 updates docs for cli path commands; minor formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
caseneuve authored and filiplajszczak committed May 21, 2021
1 parent 58c2ad3 commit e76df77
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 49 deletions.
63 changes: 36 additions & 27 deletions cli/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,19 @@ def setup(path: str, quiet: bool) -> Tuple[str, PAPath]:

@app.command()
def get(
path: str = typer.Argument(..., help="Path to PythonAnywhere file or directory"),
only_files: bool = typer.Option(False, "-f", "--files", help="List only files"),
only_dirs: bool = typer.Option(False, "-d", "--dirs", help="List only directories"),
sort_by_type: bool = typer.Option(False, "-t", "--type", help="Sort by type"),
sort_reverse: bool = typer.Option(False, "-r", "--reverse", help="Sort in reverse order"),
path: str = typer.Argument(..., help="Path to PythonAnywhere file or directory."),
only_files: bool = typer.Option(False, "-f", "--files", help="List only files."),
only_dirs: bool = typer.Option(False, "-d", "--dirs", help="List only directories."),
sort_by_type: bool = typer.Option(False, "-t", "--type", help="Sort by type."),
sort_reverse: bool = typer.Option(False, "-r", "--reverse", help="Sort in reverse order."),
raw: bool = typer.Option(
False, "-a", "--raw", help="Print API response (if PATH is file that's the only option)"
False, "-a", "--raw", help="Print API response (has effect only for directories)."
),
quiet: bool = typer.Option(False, "-q", "--quiet", help="Disable additional logging"),
quiet: bool = typer.Option(False, "-q", "--quiet", help="Disable additional logging."),
):
"""
Get contents of PATH.
If PATH points to a directory, show list of it's contents.
If PATH points to a file, print it's contents.
"""
Expand Down Expand Up @@ -73,7 +74,7 @@ def _format_tree(data, current):

for entry in reversed(data):
entry = re.sub(r"/$", "\0", entry.replace(current, ""))
chunks = [cc for cc in entry.split('/') if cc]
chunks = [cc for cc in entry.split("/") if cc]
item = chunks[-1].replace("\0", "/")
level = len(chunks) - 1
level_tracker = set([lvl for lvl in level_tracker if lvl <= level])
Expand All @@ -87,9 +88,10 @@ def _format_tree(data, current):

@app.command()
def tree(
path: str = typer.Argument(..., help="Path to PythonAnywhere file or directory"),
quiet: bool = typer.Option(False, "-q", "--quiet", help="Disable additional logging")
path: str = typer.Argument(..., help="Path to PythonAnywhere directory."),
quiet: bool = typer.Option(False, "-q", "--quiet", help="Disable additional logging.")
):
"""Show preview of directory contents at PATH in tree-like format (2 levels deep)."""
pa_path = setup(path, quiet)
tree = pa_path.tree

Expand All @@ -103,43 +105,49 @@ def tree(

@app.command()
def upload(
path: str = typer.Argument(
...,
help=(
"Full path of FILE where CONTENTS should be uploaded to -- "
"Warning: if FILE already exists, it's contents will be overwritten"
)
),
path: str = typer.Argument(..., help=("Full path of FILE where CONTENTS should be uploaded to.")),
file: typer.FileBinaryRead = typer.Option(
...,
"-c",
"--contents",
help="Path to exisitng file or stdin stream that should be uploaded to PATH"
help="Path to exisitng file or stdin stream that should be uploaded to PATH."
),
quiet: bool = typer.Option(False, "-q", "--quiet", help="Disable additional logging")
quiet: bool = typer.Option(False, "-q", "--quiet", help="Disable additional logging.")
):
"""
Upload CONTENTS to file at PATH.
If PATH points to an existing file, it will be overwritten.
"""
pa_path = setup(path, quiet)
success = pa_path.upload(file)
sys.exit(0 if success else 1)


@app.command()
def delete(
path: str = typer.Argument(..., help="Path to PythonAnywhere file or directory to be deleted"),
quiet: bool = typer.Option(False, "-q", "--quiet", help="Disable additional logging")
path: str = typer.Argument(..., help="Path to PythonAnywhere file or directory to be deleted."),
quiet: bool = typer.Option(False, "-q", "--quiet", help="Disable additional logging.")
):
"""
Delete file or directory at PATH.
If PATH points to a user owned directory all its contents will be
deleted recursively.
"""
pa_path = setup(path, quiet)
success = pa_path.delete()
sys.exit(0 if success else 1)


@app.command()
def share(
path: str = typer.Argument(..., help="Path to PythonAnywhere file to be shared"),
check: bool = typer.Option(False, "-c", "--check", help="Check sharing status"),
porcelain: bool = typer.Option(False, "-p", "--porcelain", help="Return sharing url in easy-to-parse format"),
quiet: bool = typer.Option(False, "-q", "--quiet", help="Disable logging"),
path: str = typer.Argument(..., help="Path to PythonAnywhere file."),
check: bool = typer.Option(False, "-c", "--check", help="Check sharing status."),
porcelain: bool = typer.Option(False, "-p", "--porcelain", help="Return sharing url in easy-to-parse format."),
quiet: bool = typer.Option(False, "-q", "--quiet", help="Disable logging."),
):
"""Create a sharing link to a file at PATH or check its sharing status."""
pa_path = setup(path, quiet or porcelain)
link = pa_path.get_sharing_url() if check else pa_path.share()

Expand All @@ -151,9 +159,10 @@ def share(

@app.command()
def unshare(
path: str = typer.Argument(..., help="Path to PythonAnywhere file to be unshared"),
quiet: bool = typer.Option(False, "-q", "--quiet", help="Disable additional logging")
path: str = typer.Argument(..., help="Path to PythonAnywhere file."),
quiet: bool = typer.Option(False, "-q", "--quiet", help="Disable additional logging.")
):
"""Disable sharing link for a file at PATH."""
pa_path = setup(path, quiet)
success = pa_path.unshare()
sys.exit(0 if success else 1)
2 changes: 1 addition & 1 deletion pythonanywhere/api/files_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def sharing_post(self, path):

url = self.sharing_endpoint

result = call_api(url, "POST", json={'path': path})
result = call_api(url, "POST", json={"path": path})

if result.ok:
return result.status_code, result.json()["url"]
Expand Down
34 changes: 17 additions & 17 deletions tests/test_cli_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class TestGet:
def test_exits_early_when_no_contents_for_given_path(self, mock_path):
mock_path.return_value.contents = None

result = runner.invoke(app, ["get", '~/nonexistent.file'])
result = runner.invoke(app, ["get", "~/nonexistent.file"])

assert result.exit_code == 1

Expand All @@ -67,9 +67,9 @@ def test_lists_only_directories_when_dirs_option_set(self, mock_homedir_path, ho

assert result.stdout.startswith(home_dir)
for item, value in mock_homedir_path.return_value.contents.items():
if value['type'] == 'file':
if value["type"] == "file":
assert item not in result.stdout
elif value['type'] == 'directory':
elif value["type"] == "directory":
assert item in result.stdout

def test_lists_only_files_when_files_option_set(self, mock_homedir_path, home_dir):
Expand All @@ -79,9 +79,9 @@ def test_lists_only_files_when_files_option_set(self, mock_homedir_path, home_di

assert result.stdout.startswith(home_dir)
for item, value in mock_homedir_path.return_value.contents.items():
if value['type'] == 'file':
if value["type"] == "file":
assert item in result.stdout
elif value['type'] == 'directory':
elif value["type"] == "directory":
assert item not in result.stdout

def test_reverses_directory_content_list_when_reverse_option_set(self, mock_homedir_path, home_dir):
Expand Down Expand Up @@ -132,18 +132,18 @@ class TestTree:
def test_prints_formatted_tree_when_successfull_api_call(self, mock_path, home_dir):
mock_path.return_value.path = home_dir
mock_path.return_value.tree = [
f'{home_dir}/README.txt',
f'{home_dir}/dir_one/',
f'{home_dir}/dir_one/bar.txt',
f'{home_dir}/dir_one/nested_one/',
f'{home_dir}/dir_one/nested_one/foo.txt',
f'{home_dir}/dir_one/nested_two/',
f'{home_dir}/empty/',
f'{home_dir}/dir_two/',
f'{home_dir}/dir_two/quux',
f'{home_dir}/dir_two/baz/',
f'{home_dir}/dir_three/',
f'{home_dir}/dir_three/last.txt',
f"{home_dir}/README.txt",
f"{home_dir}/dir_one/",
f"{home_dir}/dir_one/bar.txt",
f"{home_dir}/dir_one/nested_one/",
f"{home_dir}/dir_one/nested_one/foo.txt",
f"{home_dir}/dir_one/nested_two/",
f"{home_dir}/empty/",
f"{home_dir}/dir_two/",
f"{home_dir}/dir_two/quux",
f"{home_dir}/dir_two/baz/",
f"{home_dir}/dir_three/",
f"{home_dir}/dir_three/last.txt",
]

result = runner.invoke(app, ["tree", "~"])
Expand Down
8 changes: 4 additions & 4 deletions tests/test_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ def test_repr_returns_url_property_value(self, mocker):

def test_make_sharing_url_contains_pa_site_address(self, mocker):
mock_urljoin = mocker.patch("pythonanywhere.files.urljoin")
pa_path = PAPath('path')
pa_path = PAPath("path")

pa_path._make_sharing_url('rest')
pa_path._make_sharing_url("rest")

assert mock_urljoin.call_args == call(pa_path.api.base_url.split("api")[0], 'rest')
assert mock_urljoin.call_args == call(pa_path.api.base_url.split("api")[0], "rest")

def test_sanitizes_path(self):
pa_path = PAPath("~")
Expand Down Expand Up @@ -70,7 +70,7 @@ def test_warns_when_path_unavailable(self, mocker):
mock_snake = mocker.patch("pythonanywhere.files.snakesay")
mock_warning = mocker.patch("pythonanywhere.files.logger.warning")

result = PAPath('/home/different_user').contents
result = PAPath("/home/different_user").contents

assert mock_snake.call_args == call("error msg")
assert mock_warning.call_args == call(mock_snake.return_value)
Expand Down

0 comments on commit e76df77

Please sign in to comment.