Skip to content
This repository has been archived by the owner on Feb 7, 2024. It is now read-only.

Commit

Permalink
Merge pull request #451 from TezRomacH/v1.1.1
Browse files Browse the repository at this point in the history
V1.1.1
  • Loading branch information
TezRomacH authored Nov 16, 2021
2 parents 24f03c1 + 16a4d19 commit 7a92979
Show file tree
Hide file tree
Showing 44 changed files with 693 additions and 309 deletions.
28 changes: 23 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#* Variables
SHELL := /usr/bin/env bash
PYTHON := python
PYTHONPATH := `pwd`

#* Poetry
.PHONY: poetry-download
Expand All @@ -16,7 +17,7 @@ poetry-remove:
install:
poetry lock -n && poetry export --without-hashes > requirements.txt
poetry install -n
-poetry run mypy --install-types --non-interactive ./
-poetry run mypy --install-types --non-interactive hooks tests

.PHONY: pre-commit-install
pre-commit-install:
Expand All @@ -35,7 +36,8 @@ formatting: codestyle
#* Linting
.PHONY: test
test:
poetry run pytest -c pyproject.toml
PYTHONPATH=$(PYTHONPATH) poetry run pytest -c pyproject.toml --cov-report=html --cov=hooks tests/
poetry run coverage-badge -o assets/images/coverage.svg -f

.PHONY: check-codestyle
check-codestyle:
Expand All @@ -58,17 +60,33 @@ lint: test check-codestyle mypy check-safety

.PHONY: update-dev-deps
update-dev-deps:
poetry add -D bandit@latest darglint@latest "isort[colors]@latest" mypy@latest pre-commit@latest pydocstyle@latest pylint@latest pytest@latest pyupgrade@latest safety@latest
poetry add -D bandit@latest darglint@latest "isort[colors]@latest" mypy@latest pre-commit@latest pydocstyle@latest pylint@latest pytest@latest pyupgrade@latest safety@latest coverage@latest coverage-badge@latest pytest-html@latest pytest-cov@latest
poetry add -D --allow-prereleases black@latest

#* Cleaning
.PHONY: pycache-remove
pycache-remove:
find . | grep -E "(__pycache__|\.pyc|\.pyo$$)" | xargs rm -rf

.PHONY: dsstore-remove
dsstore-remove:
find . | grep -E ".DS_Store" | xargs rm -rf

.PHONY: mypycache-remove
mypycache-remove:
find . | grep -E ".mypy_cache" | xargs rm -rf

.PHONY: ipynbcheckpoints-remove
ipynbcheckpoints-remove:
find . | grep -E ".ipynb_checkpoints" | xargs rm -rf

.PHONY: pytestcache-remove
pytestcache-remove:
find . | grep -E ".pytest_cache" | xargs rm -rf

.PHONY: build-remove
build-remove:
rm -rf build/

.PHONY: clean-all
clean-all: pycache-remove build-remove
.PHONY: cleanup
cleanup: pycache-remove dsstore-remove mypycache-remove ipynbcheckpoints-remove pytestcache-remove
29 changes: 24 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
[![Pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/TezRomacH/python-package-template/blob/master/.pre-commit-config.yaml)
[![Semantic Versions](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--versions-e10079.svg)](https://github.com/TezRomacH/python-package-template/releases)
[![License](https://img.shields.io/github/license/TezRomacH/python-package-template)](https://github.com/TezRomacH/python-package-template/blob/master/LICENSE)
![Coverage Report](assets/images/coverage.svg)

Your next Python package needs a bleeding-edge project structure.
</div>

## TL;DR

```bash
cookiecutter gh:TezRomacH/python-package-template --checkout v1.1.0
cookiecutter gh:TezRomacH/python-package-template --checkout v1.1.1
```

> All you need is the latest version of cookiecutter 😉
Expand Down Expand Up @@ -65,7 +66,7 @@ pip install -U cookiecutter
then go to a directory where you want to create your project and run:

```bash
cookiecutter gh:TezRomacH/python-package-template --checkout v1.1.0
cookiecutter gh:TezRomacH/python-package-template --checkout v1.1.1
```

### Input variables
Expand Down Expand Up @@ -213,6 +214,12 @@ make check-codestyle

> Note: `check-codestyle` uses `isort`, `black` and `darglint` library
Update all dev libraries to the latest version using one comand

```bash
make update-dev-deps
```

</p>
</details>

Expand Down Expand Up @@ -247,7 +254,7 @@ make mypy
</details>

<details>
<summary>6. Tests</summary>
<summary>6. Tests with coverage badges</summary>
<p>

Run `pytest`
Expand Down Expand Up @@ -318,10 +325,22 @@ Remove package build
make build-remove
```

Or to remove pycache, build and docker image run:
Delete .DS_STORE files

```bash
make dsstore-remove
```

Remove .mypycache

```bash
make mypycache-remove
```

Or to remove all above run:

```bash
make clean-all
make cleanup
```

</p>
Expand Down
21 changes: 21 additions & 0 deletions assets/images/coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 42 additions & 23 deletions hooks/post_gen_project.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""This module is called after project is created."""

from typing import Callable, List
from typing import List

import textwrap
from pathlib import Path
Expand All @@ -19,42 +18,58 @@
# Values to generate github repository
GITHUB_USER = "{{ cookiecutter.github_name }}"

licenses = {
licences_dict = {
"MIT": "mit",
"BSD-3": "bsd3",
"GNU GPL v3.0": "gpl3",
"Apache Software License 2.0": "apache",
}


def generate_license() -> None:
"""Generate license file for the project."""
move(f"{PROJECT_DIRECTORY}/_licences/{licenses[LICENSE]}.txt", f"{PROJECT_DIRECTORY}/LICENSE")
rmtree(f"{PROJECT_DIRECTORY}/_licences/")
def generate_license(directory: Path, licence: str) -> None:
"""Generate license file for the project.
Args:
directory: path to the project directory
licence: chosen licence
"""
move(str(directory / "_licences" / f"{licence}.txt"), str(directory / "LICENSE"))
rmtree(str(directory / "_licences"))


def remove_unused_files(directory: Path, module_name: str, need_to_remove_cli: bool) -> None:
"""Remove unused files.
def remove_unused_files() -> None:
"""Remove unused files."""
Args:
directory: path to the project directory
module_name: project module name
need_to_remove_cli: flag for removing CLI related files
"""
files_to_delete: List[Path] = []

def _cli_specific_files() -> List[Path]:
return [Path(f"{PROJECT_DIRECTORY}/{PROJECT_MODULE}/__main__.py")]
return [directory / module_name / "__main__.py"]

if CREATE_EXAMPLE_TEMPLATE != "cli":
if need_to_remove_cli:
files_to_delete.extend(_cli_specific_files())

for path in files_to_delete:
path.unlink()


def print_futher_instuctions() -> None:
"""Show user what to do next after project creation."""
def print_futher_instuctions(project_name: str, github: str) -> None:
"""Show user what to do next after project creation.
Args:
project_name: current project name
github: GitHub username
"""
message = f"""
Your project {PROJECT_NAME} is created.
Your project {project_name} is created.
1) Now you can start working on it:
$ cd {PROJECT_NAME} && git init
$ cd {project_name} && git init
2) If you don't have Poetry installed run:
Expand All @@ -74,17 +89,21 @@ def print_futher_instuctions() -> None:
$ git add .
$ git commit -m ":tada: Initial commit"
$ git branch -M main
$ git remote add origin https://github.com/{GITHUB_USER}/{PROJECT_NAME}.git
$ git remote add origin https://github.com/{github}/{project_name}.git
$ git push -u origin main
"""
print(textwrap.dedent(message))


post_functions: List[Callable[[], None]] = [
generate_license,
remove_unused_files,
print_futher_instuctions,
]
def main() -> None:
generate_license(directory=PROJECT_DIRECTORY, licence=licences_dict[LICENSE])
remove_unused_files(
directory=PROJECT_DIRECTORY,
module_name=PROJECT_MODULE,
need_to_remove_cli=CREATE_EXAMPLE_TEMPLATE != "cli",
)
print_futher_instuctions(project_name=PROJECT_NAME, github=GITHUB_USER)


for fn in post_functions:
fn()
if __name__ == "__main__":
main()
50 changes: 29 additions & 21 deletions hooks/pre_gen_project.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
"""This module is called before project is created."""

from typing import Callable, List

import re
import sys

PROJECT_NAME = "{{ cookiecutter.project_name }}"
PROJECT_VERSION = "{{ cookiecutter.version }}"
LINE_LENGTH_PARAMETER = "{{ cookiecutter.line_length }}"


MODULE_REGEX = re.compile(r"^[a-z][a-z0-9\-\_]+[a-z0-9]$")
SEMVER_REGEX = re.compile(
r"""
Expand All @@ -27,28 +30,30 @@
re.VERBOSE,
)

module_name = "{{ cookiecutter.project_name }}"
version = "{{ cookiecutter.version }}"
line_length = "{{ cookiecutter.line_length }}"


def validate_project_name() -> None:
def validate_project_name(project_name: str) -> None:
"""Ensure that `project_name` parameter is valid.
Valid inputs starts with the lowercase letter.
Followed by any lowercase letters, numbers or underscores.
Args:
project_name: current project name
Raises:
ValueError: If module_name is not a valid Python module name
ValueError: If project_name is not a valid Python module name
"""
if MODULE_REGEX.fullmatch(module_name) is None:
message = f"ERROR: The project name `{module_name}` is not a valid Python module name."
if MODULE_REGEX.fullmatch(project_name) is None:
message = f"ERROR: The project name `{project_name}` is not a valid Python module name."
raise ValueError(message)


def validate_semver() -> None:
def validate_semver(version: str) -> None:
"""Ensure version in semver notation.
Args:
version: string version. For example 0.1.2 or 1.2.4
Raises:
ValueError: If version is not in semver notation
"""
Expand All @@ -57,26 +62,29 @@ def validate_semver() -> None:
raise ValueError(message)


def validate_line_length() -> None:
def validate_line_length(line_length: int) -> None:
"""Validate line_length parameter. Length should be between 50 and 300.
Args:
line_length: integer paramenter for isort and black formatters
Raises:
ValueError: If line_length isn't between 50 and 300
"""
if not (50 <= int(line_length) <= 300):
if not (50 <= line_length <= 300):
message = f"ERROR: line_length must be between 50 and 300. Got `{line_length}`."
raise ValueError(message)


validators: List[Callable[[], None]] = [
validate_project_name,
validate_semver,
validate_line_length,
]

for validator in validators:
def main() -> None:
try:
validator()
validate_project_name(project_name=PROJECT_NAME)
validate_semver(version=PROJECT_VERSION)
validate_line_length(line_length=int(LINE_LENGTH_PARAMETER))
except ValueError as ex:
print(ex)
sys.exit(1)


if __name__ == "__main__":
main()
Loading

0 comments on commit 7a92979

Please sign in to comment.