Skip to content

Commit

Permalink
Merge pull request #113 from seapagan/add-standalone-flag
Browse files Browse the repository at this point in the history
Add '--standalone' CLI flag
  • Loading branch information
seapagan committed Sep 25, 2023
2 parents 556457c + 378d0c1 commit d0c6059
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 83 deletions.
3 changes: 0 additions & 3 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ importance.
- add cmd line options to specify the project name, author, etc. so the user
doesn't have to enter them manually. `Not sure if this is needed once we add
the CLI parameters to the config file. May be useful for automation though`.
- add a command line option to specify the project type so the user doesn't have
to enter it manually. ie `--standalone` or `--package`(latter is default and
wouldn't need to be specified).
- add a command to the CLI template command to show the template files as a
tree, marking whether each file/folder is from the internal templates or the
user's templates.
Expand Down
10 changes: 9 additions & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ When it asks "Package Name?" you can choose two variants :
2. **For a stand-alone tool** that will not be uploaded to PyPI, or is not a
library, enter '-' for the package name. In this case the `main.py` will just
be placed in the project root and no package folder will be created or
referenced.
referenced. You can also specify `--standalone` on the command line to skip
this question.

For option 1 above, the App will check if the package name is available on PyPI
or if it has already been used. In the latter case, you will be asked to choose
Expand Down Expand Up @@ -84,6 +85,13 @@ If you choose to run `poetry` automatically, this will also add a customized
`mkdocs.yml` file and create a new default MkDocs site in the `docs` folder.
Some useful plugins are also installed and added to the `mkdocs.yml` file.

### `--standalone`

Generate a stand-alone script instead of a package. This will place the
`main.py` file in the project root and not create a package folder. This is
useful for creating a single script that can be run from the command line.
this is equivalent to entering `-` for the package name.

## Run `poetry install` automatically

You will be asked if you want to run `poetry install` automatically. This will
Expand Down
10 changes: 10 additions & 0 deletions py_maker/commands/new.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ def new(
Optional[bool],
typer.Option(help="Add the MkDocs boilerplate.", show_default=False),
] = None,
standalone: Annotated[
Optional[bool],
typer.Option(
"--standalone",
"-s",
help="Create a standalone project, not a package.",
show_default=False,
),
] = False,
) -> None:
"""Create a new Python project."""
options = {
Expand All @@ -47,6 +56,7 @@ def new(
"lint": settings.include_linters if lint is None else lint,
"docs": settings.include_mkdocs if docs is None else docs,
"accept_defaults": accept_defaults,
"standalone": standalone,
}

if " " in location:
Expand Down
189 changes: 110 additions & 79 deletions py_maker/pymaker.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def generate_template(self) -> None:
)

# ---------- rename or delete the 'app' dir if required ---------- #
if self.choices.package_name != "-":
if not self.choices.standalone:
Path(self.choices.project_dir / "app").rename(
self.choices.project_dir / self.choices.package_name
)
Expand Down Expand Up @@ -230,6 +230,9 @@ def post_process(self) -> None:
"""
print(output)

# ------------------------------------------------------------------------ #
# get a sanitized package name from user input. #
# ------------------------------------------------------------------------ #
def get_sanitized_package_name(self, pk_name: str) -> str:
"""Return a sanitized package name from user input."""
while True:
Expand Down Expand Up @@ -270,94 +273,75 @@ def get_sanitized_package_name(self, pk_name: str) -> str:
return name

# ------------------------------------------------------------------------ #
# The main application loop is on the .run() method. #
# accept all the default values for the project. #
# ------------------------------------------------------------------------ #
def run(self) -> None:
"""Entry point for the application."""
self.choices.project_dir = Path.cwd() / self.location

# ensure that the chosen location is empty.
if (
self.choices.project_dir.exists()
and len(os.listdir(self.choices.project_dir)) > 0
):
print(
"\n[red]Error: The chosen folder is not empty. "
"Please specify a different location.[/red]\n"
)
sys.exit(ExitErrors.FOLDER_NOT_EMPTY)

print(
"[green]Creating a new project at[/green] "
f"{self.choices.project_dir}\n"
def accept_defaults(self):
"""Accept the default values for the project."""
self.choices.name = get_title(PurePath(self.choices.project_dir).name)
self.choices.package_name = sanitize(self.choices.project_dir.name)
self.choices.description = ""
self.choices.author = self.settings.author_name
self.choices.email = self.settings.author_email
self.choices.license = self.settings.default_license

def get_input(self):
"""Get the user input for the project."""
self.choices.name = Prompt.ask(
"Name of the Application?",
default=get_title(PurePath(self.choices.project_dir).name),
)
pk_name = sanitize(self.location)

if self.options["accept_defaults"]:
self.choices.name = get_title(
PurePath(self.choices.project_dir).name
)
self.choices.package_name = sanitize(self.choices.project_dir.name)
self.choices.description = ""
self.choices.author = self.settings.author_name
self.choices.email = self.settings.author_email
self.choices.license = self.settings.default_license
self.choices.standalone = False
else:
self.choices.name = Prompt.ask(
"Name of the Application?",
default=get_title(PurePath(self.choices.project_dir).name),
)
pk_name = sanitize(self.location)
# if this is not a standalone script, ask for more details, useful
# for PypI uploads.
if not self.choices.standalone:
self.choices.package_name = self.get_sanitized_package_name(pk_name)
self.choices.homepage = Prompt.ask("Homepage URL?", default="")

# if this is not a standalone script, ask for more details, useful
# for PypI uploads.
if not self.choices.standalone:
self.choices.homepage = Prompt.ask("Homepage URL?", default="")

github_username = (
self.settings.github_username
if self.settings.github_username
else "<your GitHub username>"
)
self.choices.repository = Prompt.ask(
"Repository URL?",
default=(
f"https://github.com/{github_username}/"
f"{re.sub(r'[_.]+', '-', self.choices.package_name)}"
),
)

self.choices.description = Prompt.ask(
"Description of the Application?",
github_username = (
self.settings.github_username
if self.settings.github_username
else "<your GitHub username>"
)
self.choices.author = Prompt.ask(
"Author Name?", default=self.settings.author_name
self.choices.repository = Prompt.ask(
"Repository URL?",
default=(
f"https://github.com/{github_username}/"
f"{re.sub(r'[_.]+', '-', self.choices.package_name)}"
),
)

self.choices.email = Prompt.ask(
"Author Email?", default=self.settings.author_email
)
self.choices.license = Prompt.ask(
"Application License?",
choices=license_names,
default=self.settings.default_license,
)
self.choices.description = Prompt.ask(
"Description of the Application?",
)
self.choices.author = Prompt.ask(
"Author Name?", default=self.settings.author_name
)

if self.choices.package_name == "-":
self.choices.standalone = True
self.choices.email = Prompt.ask(
"Author Email?", default=self.settings.author_email
)
self.choices.license = Prompt.ask(
"Application License?",
choices=license_names,
default=self.settings.default_license,
)

if not self.confirm_values():
# User chose not to continue
print("\n[red]Aborting![/red]")
sys.exit(ExitErrors.USER_ABORT)
if not self.confirm_values():
# User chose not to continue
print("\n[red]Aborting![/red]")
sys.exit(ExitErrors.USER_ABORT)

print()
print()

self.create_folders()
self.generate_template()
# ------------------------------------------------------------------------ #
# run 'poetry install' if required. #
# ------------------------------------------------------------------------ #
def run_poetry(self):
"""Run poetry install if required.
# run poetry install if required
We also create the MkDocs project if enabled.
"""
if self.options["accept_defaults"] or Confirm.ask(
"\nShould I Run 'poetry install' now?", default=True
):
Expand All @@ -375,10 +359,14 @@ def run(self) -> None:
MKDOCS_CONFIG.format(name=self.choices.name)
)

self.create_git_repo()
# ------------------------------------------------------------------------ #
# optionally install and update the pre-commit hooks #
# ------------------------------------------------------------------------ #
def install_precommit(self):
"""Install pre-commit hooks - IF poetry and git are run.
# install and update pre-commit hooks IF poetry and git are run.
# this would fail without either of them.
This would fail without either of them.
"""
if (
self.poetry_is_run
and self.git_is_run
Expand Down Expand Up @@ -420,4 +408,47 @@ def run(self) -> None:
"""
)

# ------------------------------------------------------------------------ #
# The main application loop is on the .run() method. #
# ------------------------------------------------------------------------ #
def run(self) -> None:
"""Run the full process to create the project.
We get input from the user, as well as the options passed in from the
command line. We then create the project skeleton, copy the template
files, create the license file, create the git repo, optionally run
'poetry install' and install the 'pre-commit' hooks.
"""
self.choices.project_dir = Path.cwd() / self.location

# ensure that the chosen location is empty.
if (
self.choices.project_dir.exists()
and len(os.listdir(self.choices.project_dir)) > 0
):
print(
"\n[red]Error: The chosen folder is not empty. "
"Please specify a different location.[/red]\n"
)
sys.exit(ExitErrors.FOLDER_NOT_EMPTY)

print(
"[green]Creating a new project at[/green] "
f"{self.choices.project_dir}\n"
)

self.choices.standalone = True if self.options["standalone"] else False

if self.options["accept_defaults"]:
self.accept_defaults()
else:
self.get_input()

self.create_folders()
self.generate_template()

self.run_poetry()
self.create_git_repo()
self.install_precommit()

self.post_process()

0 comments on commit d0c6059

Please sign in to comment.