Add async pieces.
This commit is contained in:
parent
79e06ea46f
commit
32f9588e26
11 changed files with 241 additions and 37 deletions
46
${REPO_NAME_SNAKE}/__init__.py
Normal file
46
${REPO_NAME_SNAKE}/__init__.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
import argparse
|
||||
import asyncio
|
||||
import sys
|
||||
|
||||
import uvloop
|
||||
|
||||
__version__ = "0.0.1"
|
||||
|
||||
|
||||
async def main(args: argparse.Namespace) -> None:
|
||||
"""
|
||||
Main function of the program (stub).
|
||||
|
||||
This function serves as ${REPO_NAME_SNAKE}'s critical path for primary logic.
|
||||
It receives an `argparse.Namespace` object as input from invoke_maiun, which includes
|
||||
the command-line arguments that were defined and parsed in the `run` function.
|
||||
|
||||
Args:
|
||||
args (argparse.Namespace): The command-line arguments object.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def invoke_main(args: argparse.Namespace) -> None:
|
||||
"""
|
||||
Function to invoke the main function using asyncio and uvloop (stub).
|
||||
|
||||
This function serves as a bridge to run the async main function in
|
||||
a uvloop event loop. It handles the differences in asyncio API across
|
||||
different Python versions.
|
||||
|
||||
Args:
|
||||
args (argparse.Namespace): The command-line arguments object.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
if sys.version_info >= (3, 11):
|
||||
with asyncio.Runner(loop_factory=uvloop.new_event_loop) as runner:
|
||||
runner.run(main(args))
|
||||
else:
|
||||
uvloop.install()
|
||||
asyncio.run(main(args))
|
44
${REPO_NAME_SNAKE}/cli.py
Normal file
44
${REPO_NAME_SNAKE}/cli.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
from argparse import ArgumentParser
|
||||
from pathlib import Path
|
||||
|
||||
from . import __version__
|
||||
from .logger import logger
|
||||
from .validation import validate_file_folder
|
||||
|
||||
|
||||
def run() -> None:
|
||||
"""
|
||||
Command-line interface for ${REPO_NAME_SNAKE}.
|
||||
|
||||
This function defines and handles command-line arguments for the program.
|
||||
The available options are:
|
||||
|
||||
--version: Show the version of the program and exit.
|
||||
--verbose: Enable verbose logging. This will produce detailed output messages.
|
||||
--output/-o: Specify the output file. This must be a valid file path.
|
||||
paths: Specify any number of existing files or directories to be processed.
|
||||
These paths must point to existing files or directories.
|
||||
|
||||
Upon parsing the command-line arguments, the function configures the logger
|
||||
and invokes the `main` function with the parsed arguments.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
# your code here
|
||||
parser = ArgumentParser(description="${REPO_DESCRIPTION}")
|
||||
parser.add_argument("--version", action="version", version=__version__)
|
||||
parser.add_argument("--verbose", action="store_true", help="Enable verbose logging")
|
||||
parser.add_argument("--output", "-o", type=Path, help="Specify output file")
|
||||
parser.add_argument(
|
||||
"paths",
|
||||
nargs="+",
|
||||
type=validate_file_folder,
|
||||
help="Specify any number of existing files or directories to be processed.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.verbose:
|
||||
logger.debug("Verbose logging enabled")
|
||||
|
||||
run(args)
|
4
${REPO_NAME_SNAKE}/logger.py
Normal file
4
${REPO_NAME_SNAKE}/logger.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
import logging
|
||||
|
||||
logging.basicConfig(format="%(asctime)s - %(message)s", datefmt="%d-%b-%y %H:%M:%S")
|
||||
logger = logging.getLogger(__name__)
|
12
${REPO_NAME_SNAKE}/validation.py
Normal file
12
${REPO_NAME_SNAKE}/validation.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from pathlib import Path
|
||||
|
||||
from aiopath import AsyncPath
|
||||
|
||||
|
||||
def validate_file_folder(value: str) -> Path:
|
||||
file_folder_path = Path(value)
|
||||
if not file_folder_path.exists():
|
||||
raise FileNotFoundError(f"No such file or folder: {value}")
|
||||
if not file_folder_path.is_file() and not file_folder_path.is_dir():
|
||||
raise TypeError(f"Not a file or directory: {value}")
|
||||
return AsyncPath(value)
|
3
.flake8
Normal file
3
.flake8
Normal file
|
@ -0,0 +1,3 @@
|
|||
[flake8]
|
||||
max-line-length = 160
|
||||
exclude = docs/*, build/*, .git, __pycache__, build
|
40
.gitignore
vendored
40
.gitignore
vendored
|
@ -28,8 +28,6 @@ share/python-wheels/
|
|||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
|
@ -83,34 +81,10 @@ target/
|
|||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
# PDM
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
# PEP 582
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
|
@ -154,11 +128,7 @@ dmypy.json
|
|||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
.idea/
|
||||
|
||||
# ---> macOS
|
||||
# General
|
||||
|
@ -167,7 +137,8 @@ cython_debug/
|
|||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
@ -228,4 +199,3 @@ $RECYCLE.BIN/
|
|||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
|
|
35
.pre-commit-config.yaml
Normal file
35
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,35 @@
|
|||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v3.2.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
- repo: https://github.com/asottile/reorder_python_imports
|
||||
rev: v3.9.0
|
||||
hooks:
|
||||
- id: reorder-python-imports
|
||||
args: [--application-directories, '.:${REPO_NAME_SNAKE}', --py39-plus]
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3.11
|
||||
- repo: https://github.com/PyCQA/bandit
|
||||
rev: 1.7.5
|
||||
hooks:
|
||||
- id: bandit
|
||||
args: ["-c", "pyproject.toml"]
|
||||
additional_dependencies: ["bandit[toml]"]
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 6.0.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: version
|
||||
name: Update version
|
||||
entry: ./pre-commit.sh
|
||||
language: script
|
||||
files: 'pyproject.toml'
|
34
README.md
34
README.md
|
@ -1,3 +1,33 @@
|
|||
# python-module
|
||||
<div align="center">
|
||||
# ${REPO_NAME_SNAKE}
|
||||
|
||||
Template for a new Python project.
|
||||
**${REPO_NAME_SNAKE}** is a...
|
||||
|
||||
Python project!<br/>
|
||||
|
||||
[Installation](#installation) •
|
||||
[Examples](#examples) •
|
||||
[Contributing](#contributing) •
|
||||
[License](#license)
|
||||
</div>
|
||||
|
||||
## Installation
|
||||
|
||||
Install and run ${REPO_NAME_SNAKE} using the below commands or their equivalents for your environment.
|
||||
|
||||
```bash
|
||||
pip3 install -U ${REPO_HTTPS_URL}
|
||||
${REPO_NAME_SNAKE}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
TBD
|
||||
|
||||
## Contributing
|
||||
|
||||
If you would like to contribute to this project, feel free to submit a pull request or open an issue on GitHub.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the current AGPL 3.0+ License. See the `LICENSE` file for details.
|
||||
|
|
21
pre-commit.sh
Executable file
21
pre-commit.sh
Executable file
|
@ -0,0 +1,21 @@
|
|||
#!/bin/bash
|
||||
|
||||
PROJECT_ROOT=$(git rev-parse --show-toplevel)
|
||||
VERSION=$(grep 'version =' $PROJECT_ROOT/pyproject.toml | head -1 | cut -d '"' -f 2)
|
||||
INIT_FILE="$PROJECT_ROOT/${REPO_NAME_SNAKE}/${REPO_NAME_SNAKE}/__init__.py"
|
||||
PYPROJECT_FILE="$PROJECT_ROOT/pyproject.toml"
|
||||
|
||||
# Modify the CLI line to remove quotes in pyproject.toml
|
||||
sed -i.bak "s/\"${REPO_NAME_SNAKE}\" = \"${REPO_NAME_SNAKE}\.cli:run\"/${REPO_NAME_SNAKE} = \"${REPO_NAME_SNAKE}\.cli:run\"/" $PYPROJECT_FILE && \
|
||||
rm "$PYPROJECT_FILE.bak"
|
||||
|
||||
# Check if __init__.py file exists for template purposes
|
||||
# This can be removed now that the template is cloned
|
||||
if [ ! -f "$INIT_FILE" ]; then
|
||||
echo "__init__.py does not exist yet, exiting."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Portable workaround for macOS/Linux sed differences
|
||||
sed -i.bak "s/__version__ = .*/__version__ = \"$VERSION\"/" $INIT_FILE && \
|
||||
rm "$INIT_FILE.bak"
|
34
pyproject.toml
Normal file
34
pyproject.toml
Normal file
|
@ -0,0 +1,34 @@
|
|||
[build-system]
|
||||
requires = ["setuptools>=68.0"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "${REPO_NAME_SNAKE}"
|
||||
version = "0.0.1"
|
||||
authors = [{ name = "${REPO_OWNER_SNAKE}", email = "git@nixon.mozmail.com" }]
|
||||
description = "${REPO_DESCRIPTION}"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.9"
|
||||
license = { text = "MIT" }
|
||||
dependencies = [
|
||||
"loguru>=0.7.0",
|
||||
"uvloop>=0.17.0",
|
||||
"aiopath>=0.6.11",
|
||||
"asyncstdlib>=3.10.8",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
"${REPO_NAME_SNAKE}" = "${REPO_NAME_SNAKE}.cli:run"
|
||||
|
||||
[project.urls]
|
||||
homepage = "${REPO_LINK}"
|
||||
repository = "${REPO_LINK}"
|
||||
|
||||
[tool.setuptools]
|
||||
py-modules = ["${REPO_NAME_SNAKE}"]
|
||||
|
||||
[tool.bandit]
|
||||
exclude_dirs = ["/docs", "/build"]
|
||||
|
||||
[tool.black]
|
||||
line-length = 120
|
5
template
Normal file
5
template
Normal file
|
@ -0,0 +1,5 @@
|
|||
**.py
|
||||
.pre-commit-config.yaml
|
||||
README.md
|
||||
pyproject.toml
|
||||
pre-commit.sh
|
Loading…
Reference in a new issue