Skip to content

Commit

Permalink
Merge pull request #648 from FabienArcellier/WF-95-improve-messaging-…
Browse files Browse the repository at this point in the history
…when-trying-to-open-a-folder-where-a-project-hasnt-been-initialized

feat: improve messaging when trying to open a folder where a project hasn't been initialized - WF-95
  • Loading branch information
ramedina86 authored Dec 2, 2024
2 parents 660715c + ac67d48 commit 76c600f
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 16 deletions.
49 changes: 33 additions & 16 deletions src/writer/command_line.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import logging
import os
import shutil
import sys
Expand All @@ -7,7 +6,7 @@
import click

import writer.serve
from writer import VERSION
from writer import VERSION, wf_project
from writer.deploy import cloud, deploy

CONTEXT_SETTINGS = {'help_option_names': ['-h', '--help']}
Expand All @@ -27,8 +26,13 @@ def run(path: str, host: str, port: Optional[int]):
"""Run the app from PATH folder in run mode."""

abs_path = os.path.abspath(path)
if not os.path.isdir(abs_path):
raise click.ClickException("A path to a folder containing a Writer Framework app is required. For example: writer run my_app")
if wf_project.is_project(path) is False and \
wf_project.can_create_project(path) is True:
raise click.ClickException(f"There’s no Writer Framework project at this location, create a new one with `writer create {path}`")

if wf_project.is_project(path) is False and \
wf_project.can_create_project(path) is False:
raise click.ClickException(f"There’s no Writer Framework project at this location : {abs_path}")

writer.serve.serve(
abs_path, mode="run", port=port, host=host, enable_server_setup=True)
Expand All @@ -38,13 +42,31 @@ def run(path: str, host: str, port: Optional[int]):
@click.option('--port', default=None, help="Port to run the app on")
@click.option('--enable-remote-edit', help="Set this flag to allow non-local requests in edit mode.", is_flag=True)
@click.option('--enable-server-setup', help="Set this flag to enable server setup hook in edit mode.", is_flag=True)
@click.option("--no-interactive", help="Set the flask to ask the app to run without asking anything to the user", is_flag=True)
@click.argument('path')
def edit(path: str, port: Optional[int], host: str, enable_remote_edit: bool, enable_server_setup: bool):
def edit(
path: str,
port: Optional[int],
host: str,
enable_remote_edit: bool,
enable_server_setup: bool,
no_interactive: bool
):
"""Run the app from PATH folder in edit mode."""

abs_path = os.path.abspath(path)
if not os.path.isdir(abs_path):
raise click.ClickException("A path to a folder containing a Writer Framework app is required. For example: writer edit my_app")
if wf_project.is_project(path) is False and \
wf_project.can_create_project(path) is True and \
no_interactive is False:
click.confirm("There’s no Writer Framework project at this location, would you like to create a new one ?", default=False, abort=True)
create_app(path, template_name="default", overwrite=False)

if wf_project.is_project(path) is False and \
wf_project.can_create_project(path) is True:
raise click.ClickException(f"There’s no Writer Framework project at this location, create a new one with `writer create {path}`")

if wf_project.is_project(path) is False and \
wf_project.can_create_project(path) is False:
raise click.ClickException(f"There’s no Writer Framework project at this location : {abs_path}")

writer.serve.serve(
abs_path, mode="edit", port=port, host=host,
Expand Down Expand Up @@ -81,19 +103,14 @@ def create_app(app_path: str, template_name: Optional[str], overwrite=False):
if template_name is None:
template_name = "default"

is_folder_created = os.path.exists(app_path)
is_folder_empty = True if not is_folder_created else len(os.listdir(app_path)) == 0

if not overwrite and not is_folder_empty:
logging.error("The target folder must be empty or not already exist.")
sys.exit(1)
if wf_project.can_create_project(path=app_path) is False:
raise click.ClickException("The target folder must be empty or not already exist.")

server_path = os.path.dirname(__file__)
template_path = os.path.join(server_path, "app_templates", template_name)

if not os.path.exists(template_path):
logging.error(f"Template { template_name } couldn't be found.")
sys.exit(1)
raise click.ClickException(f"Template { template_name } couldn't be found.")

shutil.copytree(template_path, app_path, dirs_exist_ok=True)
# create/update requirements.txt and add writer to it
Expand Down
29 changes: 29 additions & 0 deletions src/writer/wf_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,3 +292,32 @@ def _start_process_write_files_async_process(context: WfProjectContext, save_int
write_files(app_path, metadata, components)

time.sleep(save_interval)


def is_project(path: str) -> bool:
"""
Returns True if the path is a Writer Framework project.
>>> wf_project.is_project('app/hello')
"""
has_main_py = os.path.isfile(os.path.join(path, "main.py"))
has_wf_directory = os.path.isdir(os.path.join(path, ".wf"))
has_ui_json_file = os.path.isfile(os.path.join(path, "ui.json"))

return has_main_py and (has_wf_directory or has_ui_json_file)


def can_create_project(path: str) -> bool:
"""
Returns True the path does not contain a Writer Framework project and
it is possible to create one automatically.
>>> wf_project.can_create_project('app/hello')
"""
if not os.path.isdir(path):
return True

if len(os.listdir(path)) == 0:
return True

return False
61 changes: 61 additions & 0 deletions tests/backend/test_wf_project.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import io
import os
import shutil
import tempfile
Expand Down Expand Up @@ -126,3 +127,63 @@ def test_wf_project_migrate_obsolete_ui_json_should_migrate_ui_json_into_wf_dire
assert not os.path.isfile(os.path.join(tmp_app_dir, 'ui.json'))
assert os.path.isfile(os.path.join(tmp_app_dir, '.wf', 'metadata.json'))
assert os.path.isfile(os.path.join(tmp_app_dir, '.wf', 'components-root.jsonl'))


def test_wf_project_is_project_work_on_current_project():
with tempfile.TemporaryDirectory('wf_project_migrate_obsolete_ui_json') as tmp_app_dir:
shutil.copytree(testobsoleteapp, tmp_app_dir, dirs_exist_ok=True)

# When
is_wf_project = wf_project.is_project(tmp_app_dir)

# Then
assert is_wf_project is True


def test_wf_project_is_project_work_on_obsolete_project():
with tempfile.TemporaryDirectory('test_wf_project_write_files') as tmp_app_dir:
shutil.copytree(test_app_dir, tmp_app_dir, dirs_exist_ok=True)

# When
is_wf_project = wf_project.is_project(tmp_app_dir)

# Then
assert is_wf_project is True


def test_wf_project_can_create_project_does_not_work_on_project_with_ui_json():
with tempfile.TemporaryDirectory('test_wf_project_write_files') as tmp_app_dir:
shutil.copytree(testobsoleteapp, tmp_app_dir, dirs_exist_ok=True)
os.remove(os.path.join(tmp_app_dir, 'main.py'))

# When
is_wf_project = wf_project.is_project(tmp_app_dir)
can_create_wf_project = wf_project.can_create_project(tmp_app_dir)

# Then
assert is_wf_project is False
assert can_create_wf_project is False


def test_wf_project_can_create_project_does_not_work_on_project_with_a_directory():
with tempfile.TemporaryDirectory('test_wf_project_can_create_project') as tmp_app_dir:
io.open(os.path.join(tmp_app_dir, 'file'), 'w').close()

# When
is_wf_project = wf_project.is_project(tmp_app_dir)
can_create_wf_project = wf_project.can_create_project(tmp_app_dir)

# Then
assert is_wf_project is False
assert can_create_wf_project is False


def test_wf_project_can_create_project_on_empty_directory():
with tempfile.TemporaryDirectory('test_wf_project_can_create_project') as tmp_app_dir:
# When
is_wf_project = wf_project.is_project(tmp_app_dir)
can_create_wf_project = wf_project.can_create_project(tmp_app_dir)

# Then
assert is_wf_project is False
assert can_create_wf_project is True

0 comments on commit 76c600f

Please sign in to comment.