diff --git a/.gitattributes b/.gitattributes index d5799bd..f782c04 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,6 @@ # GitHub syntax highlighting pixi.lock linguist-language=YAML +*.py.jinja linguist-language=Python +*.toml.jinja linguist-language=TOML +*.md.jinja linguist-language=Markdown +*.yaml.jinja linguist-language=YAML \ No newline at end of file diff --git a/PACKAGE_TEMPLATE/.github/workflows/updates.yaml b/PACKAGE_TEMPLATE/.github/workflows/updates.yaml new file mode 100644 index 0000000..0522e59 --- /dev/null +++ b/PACKAGE_TEMPLATE/.github/workflows/updates.yaml @@ -0,0 +1,39 @@ +# https://pixi.sh/latest/advanced/updates_github_actions/ +name: Update lockfiles + +permissions: + contents: write + pull-requests: write + +on: + workflow_dispatch: + schedule: + - cron: 0 5 1 * * + +jobs: + pixi-update: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up pixi + uses: prefix-dev/setup-pixi@v0.8.1 + with: + run-install: false + + - name: Update lockfiles + run: | + set -o pipefail + pixi update --json | pixi exec pixi-diff-to-markdown >> diff.md + + - name: Create pull request + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore: Update pixi lockfile" + title: "chore: Update pixi lockfile" + body-path: diff.md + branch: update-pixi + base: main + labels: pixi + delete-branch: true + add-paths: pixi.lock diff --git a/PACKAGE_TEMPLATE/.gitignore b/PACKAGE_TEMPLATE/.gitignore index eac7e5a..1c8a509 100644 --- a/PACKAGE_TEMPLATE/.gitignore +++ b/PACKAGE_TEMPLATE/.gitignore @@ -152,19 +152,12 @@ dmypy.json # Cython debug symbols 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/ .pixi coverage-report recipe.yaml .ruff_cache -src/testmypixipkg/__pycache__/__init__.cpython-312.pyc **/__pycache__/* *./**/.pyc **.pyc \ No newline at end of file diff --git a/PACKAGE_TEMPLATE/README.md.jinja b/PACKAGE_TEMPLATE/README.md.jinja index 0d96f28..6b5d298 100644 --- a/PACKAGE_TEMPLATE/README.md.jinja +++ b/PACKAGE_TEMPLATE/README.md.jinja @@ -3,3 +3,19 @@ ============================= {{ package_description }} + +[![pixi-badge](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/prefix-dev/pixi/main/assets/badge/v0.json&style=flat-square)](https://github.com/prefix-dev/pixi) +[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json&style=flat-square)](https://github.com/astral-sh/ruff) +[![Built with Material for MkDocs](https://img.shields.io/badge/mkdocs--material-gray?logo=materialformkdocs&style=flat-square)](https://github.com/squidfunk/mkdocs-material) + +![GitHub repo size](https://img.shields.io/github/repo-size/{{ github_username }}/{{ project_slug }}?style=flat-square) +![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/{{ github_username }}/{{ project_slug }}?style=flat-square) +![GitHub last commit](https://img.shields.io/github/last-commit/{{ github_username }}/{{ project_slug }}?style=flat-square) +![GitHub commit activity](https://img.shields.io/github/commit-activity/{{ github_username }}/{{ project_slug }}?style=flat-square) +![GitHub issues](https://img.shields.io/github/issues/{{ github_username }}/{{ project_slug }}?style=flat-square) +![GitHub pull requests](https://img.shields.io/github/issues-pr/{{ github_username }}/{{ project_slug }}?style=flat-square) +![GitHub contributors](https://img.shields.io/github/contributors/{{ github_username }}/{{ project_slug }}?style=flat-square) +![GitHub stars](https://img.shields.io/github/stars/{{ github_username }}/{{ project_slug }}?style=flat-square) +![GitHub forks](https://img.shields.io/github/forks/{{ github_username }}/{{ project_slug }}?style=flat-square) + +![GitHub release (latest by date)](https://img.shields.io/github/v/release/{{ github_username }}/{{ project_slug }}?style=flat-square) diff --git a/PACKAGE_TEMPLATE/config/coverage.toml.jinja b/PACKAGE_TEMPLATE/config/coverage.toml.jinja new file mode 100644 index 0000000..50177d2 --- /dev/null +++ b/PACKAGE_TEMPLATE/config/coverage.toml.jinja @@ -0,0 +1,34 @@ +[tool.coverage.run] +# Specify the source code directories to measure +# Ensures coverage only tracks relevant package code +source = ["{{ package_name }}"] + +# Omit specific files or directories from coverage +# Excludes non-relevant files from coverage reporting +omit = [ + "tests/*", + "*/__init__.py", +] + +[tool.coverage.report] +# Exclude lines matching these patterns +# Ignores specific patterns from coverage calculations +exclude_lines = [ + # Standard pragma to ignore code blocks + "pragma: no cover", + + # Don't complain about missing debug-only code + "def __repr__", + "if self.debug", + + # Don't complain if tests don't hit defensive assertion code + "raise AssertionError", + "raise NotImplementedError", + + # Don't complain if non-runnable code isn't run + "if __name__ == .__main__.:", + + # Type checking related + "if TYPE_CHECKING:", + "pass", +] diff --git a/PACKAGE_TEMPLATE/config/hatch.toml.jinja b/PACKAGE_TEMPLATE/config/hatch.toml.jinja new file mode 100644 index 0000000..6488adc --- /dev/null +++ b/PACKAGE_TEMPLATE/config/hatch.toml.jinja @@ -0,0 +1,31 @@ +[version] +# Path to file containing version string +# https://hatch.pypa.io/latest/version/#sources +path = "src/{{ package_name }}/__init__.py" + +[build] +# Output directory for built distributions +# https://hatch.pypa.io/latest/config/build/#output-directory +directory = "dist" + +# Include package data files +# https://hatch.pypa.io/latest/config/build/#include-package-data +include-package-data = true + +# Generate reproducible builds +# https://hatch.pypa.io/latest/config/build/#reproducible +reproducible = false + +[build.targets.wheel] +# A Wheel is a built distribution format +# https://packaging.python.org/en/latest/specifications/binary-distribution-format/ +# https://hatch.pypa.io/latest/plugins/builder/wheel/ + +packages = ["src/{{ package_name }}"] + +[build.targets.sdist] +# A Source Distribution (or "sdist") is a built distribution format +# https://packaging.python.org/en/latest/specifications/source-distribution-format/ +# https://hatch.pypa.io/latest/plugins/builder/sdist/ + +include = ["src/{{ package_name }}"] diff --git a/PACKAGE_TEMPLATE/config/mypy.ini.jinja b/PACKAGE_TEMPLATE/config/mypy.ini.jinja new file mode 100644 index 0000000..7751722 --- /dev/null +++ b/PACKAGE_TEMPLATE/config/mypy.ini.jinja @@ -0,0 +1,63 @@ +[mypy] + +# Specify the source code directories to analyze +files = src + +# Exclude files from analysis +exclude = tests + +# Specify the Python version to use for type checking +# This ensures mypy uses the correct Python version's type system +python_version = {{ python_version }} + +# Set the cache directory +cache_dir = .cache/mypy + +# Warn when returning Any from a function with a non-Any return type +# Helps maintain type safety by catching implicit Any returns +warn_return_any = True + +# Warn about mypy config options that are unused +# Helps keep the mypy configuration clean and relevant +warn_unused_configs = True + +# Require type annotations for all function definitions +# Enforces explicit type annotations, improving code clarity and type safety +disallow_untyped_defs = True + +# Disallow functions with incomplete type annotations +# Ensures all function arguments and return types are properly annotated +disallow_incomplete_defs = True + +# Type check the interior of functions without type annotations +# Helps catch type errors even in functions without annotations +check_untyped_defs = True + +# Require type annotations for decorators +# Ensures type safety when using custom decorators +disallow_untyped_decorators = True + +# Make Optional explicit - no implicit Optional from None default +# Improves code clarity by requiring explicit Optional annotations +no_implicit_optional = True + +# Warn about casting that does not change the type +# Helps identify and remove unnecessary type casts +warn_redundant_casts = True + +# Warn about unneeded '# type: ignore' comments +# Helps maintain clean code by identifying outdated type ignores +warn_unused_ignores = True + +# Warn about functions that end without returning +# Catches potential bugs where functions don't return as expected +warn_no_return = True + +# Warn about code that is never executed +# Helps identify dead code and logical errors +warn_unreachable = True + +[mypy-pytest.*] +# Ignore missing type hints in pytest package +# Prevents errors when pytest's types are not available +ignore_missing_imports = True \ No newline at end of file diff --git a/PACKAGE_TEMPLATE/config/pytest.ini.jinja b/PACKAGE_TEMPLATE/config/pytest.ini.jinja new file mode 100644 index 0000000..63cd0a1 --- /dev/null +++ b/PACKAGE_TEMPLATE/config/pytest.ini.jinja @@ -0,0 +1,64 @@ +[pytest] +# Minimum required pytest version for this configuration +# Ensures compatibility with the specified features +minversion = 7.0 + +# Additional command-line options to always include +# Sets default behavior for test runs +addopts = + # Show detailed test progress and results + # Improves visibility of test execution + --verbose + # Show local variables in tracebacks + # Helps with debugging failed tests + --showlocals + # Generate coverage report + # Tracks code coverage during test execution + --cov={{ package_name }} + # Output coverage report in terminal + # Provides immediate feedback on coverage + --cov-report=term-missing + # Generate XML coverage report + # Creates detailed coverage report for upload to code coverage services + --cov-report=xml:coverage-report/coverage.xml + # Generate HTML coverage report + # Creates detailed coverage report for analysis + --cov-report=html:coverage-report/htmlcov + # Point to coverage config file + # Allows customization of coverage report generation + --cov-config=config/coverage.toml + +# Patterns for test discovery +# Defines which files are considered test files +testpaths = + tests + {{ package_name }} + +# Python paths to add to PYTHONPATH +# Ensures test modules can import package code +pythonpath = . + +# Markers for categorizing tests +# Allows running specific test categories +markers = + slow: marks tests as slow (deselect with '-m "not slow"') + integration: marks tests as integration tests + unit: marks tests as unit tests + +# Files to ignore during test collection +# Excludes specified files from testing +norecursedirs = *.egg .eggs dist build docs .tox .git __pycache__ + +# Test filename patterns +# Defines patterns for test file discovery +python_files = test_*.py *_test.py *_tests.py + +# Configure console output style +# Sets the format of test result output +console_output_style = progress + +# {% if enable_xdist %} +# #Number of processes for parallel testing +# #Speeds up test execution on multi-core systems +# numprocesses = auto +# {% endif %} \ No newline at end of file diff --git a/PACKAGE_TEMPLATE/config/releaserc.toml.jinja b/PACKAGE_TEMPLATE/config/releaserc.toml.jinja new file mode 100644 index 0000000..ba81b5b --- /dev/null +++ b/PACKAGE_TEMPLATE/config/releaserc.toml.jinja @@ -0,0 +1,34 @@ +[semantic_release] +version_variables = ["src/{{ package_name }}/__init__.py:__version__"] +version_toml = ["pyproject.toml:project.version"] +upload_to_release = true +remove_dist = false +commit_message = "chore(sem-ver): {version}" +patch_without_tag = false +major_on_zero = false + +[semantic_release.branches.main] +match = "(main|master)" + +[semantic_release.branches.dev] +# any branch that contains "dev" will be considered a dev branch +match = "(dev|develop)" +prerelease = true +prerelease_token = "rc" + +[semantic_release.commit_parser_options] +allowed_tags = [ + "build", + "chore", + "ci", + "docs", + "feat", + "fix", + "perf", + "style", + "refactor", + "test", +] +minor_tags = ["feat"] +patch_tags = ["fix", "perf"] +default_bump_level = 0 \ No newline at end of file diff --git a/PACKAGE_TEMPLATE/config/ruff.toml.jinja b/PACKAGE_TEMPLATE/config/ruff.toml.jinja new file mode 100644 index 0000000..8802ff9 --- /dev/null +++ b/PACKAGE_TEMPLATE/config/ruff.toml.jinja @@ -0,0 +1,118 @@ +########################################################################### +# RUFF CONFIGURATION +# Ruff is an extremely fast Python linter and code formatter, written in Rust. +# https://docs.astral.sh/ruff/settings/ + +# Show detailed output with rule codes and explanations +output-format = "full" +# Automatically fix issues when possible +fix = true + +# Directories to exclude from all checks +extend-exclude = [ + "docs/*", # Skip documentation files + "tests/*", # Skip test files +] + +# Set the ruff cache directory +cache-dir = ".cache/ruff" + +########################################################################### +# RUFF LINTING +# https://docs.astral.sh/ruff/linter/ + +[lint] + +select = [ + ########################################################################### + # TYPE ANNOTATIONS + # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann + "ANN", # Enforces type annotations for functions, improving code clarity and IDE support + # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tch + "TCH", # Ensures consistent usage of typing constructs and validates type checking patterns + + ########################################################################### + # IMPORTS + # https://docs.astral.sh/ruff/rules/#isort-i + "I", # Automatically sorts and formats imports according to PEP 8 + # https://docs.astral.sh/ruff/rules/#flake8-import-conventions-icn + "ICN", # Standardizes import conventions, especially for commonly confused modules + # https://docs.astral.sh/ruff/rules/#flake8-tidy-imports-tid + "TID", # Prevents banned imports and enforces consistent import patterns + + ########################################################################### + # CODE QUALITY + # https://docs.astral.sh/ruff/rules/#flake8-bugbear-b + "B", # Catches common bugs and design problems before they cause issues + # https://docs.astral.sh/ruff/rules/#flake8-builtins-a + "A", # Prevents naming conflicts with Python built-ins + # https://docs.astral.sh/ruff/rules/#flake8-commas-com + "COM", # Enforces consistent comma usage in multi-line structures + # https://docs.astral.sh/ruff/rules/#flake8-debugger-t10 + "T10", # Prevents committed debugging code like breakpoint() statements + # https://docs.astral.sh/ruff/rules/#flake8-print-t20 + "T20", # Catches print statements that should be proper logging calls + # https://docs.astral.sh/ruff/rules/#flake8-errmsg-em + "EM", # Ensures exception messages are properly formatted + # https://docs.astral.sh/ruff/rules/#tryceratops-try + "TRY", # Prevent try-except handling antipatterns https://blog.guilatrova.dev/handling-exceptions-in-python-like-a-pro + # https://docs.astral.sh/ruff/rules/#refurb-furb + "FURB", # refurbish and modernize codebases + # https://docs.astral.sh/ruff/rules/#pandas-vet-pd + "PD", # opinionated pandas code linting + + ########################################################################### + # STANDARDS & STYLE + # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth + "PTH", # Modernizes code by using pathlib instead of os.path + # https://docs.astral.sh/ruff/rules/#pylint-pl + "PL", # Applies Pylint's battle-tested code quality rules + # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim + "SIM", # Identifies code that can be simplified or made more idiomatic + # https://docs.astral.sh/ruff/rules/#pyflakes-f + "F", # Finds logical errors like undefined names and unused imports + # https://docs.astral.sh/ruff/rules/#pep8-naming-n + "N", # Enforces PEP 8 naming conventions for consistency +] + +ignore = [ + "PLR0913", # Permits functions with many arguments when necessary + "D100", # Allows modules without docstrings + # https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules + "COM812", # Avoids conflict with Ruff formatter trailing comma rules + "D206" # Allows docstrings to be formatted flexibly +] + +# https://docs.astral.sh/ruff/settings/#isort +[lint.isort] +# Define project-specific imports for better organization +known-first-party = ["{{ package_name }}"] + +# https://docs.astral.sh/ruff/settings/#pydocstyle +[lint.pydocstyle] +# Use NumPy docstring convention for clear, structured documentation +convention = "numpy" + +# https://docs.astral.sh/ruff/settings/#per-file-ignores +[lint.per-file-ignores] +# Allow assert statements in test files +"tests/*" = ["S101"] + +# https://docs.astral.sh/ruff/settings/#mccabe +[lint.mccabe] +# Set maximum cyclomatic complexity to prevent overly complex functions +max-complexity = 10 + +########################################################################### +# FORMATTING +# https://docs.astral.sh/ruff/formatter/ + +[format] +# Use single quotes for string literals +quote-style = "single" + +# Use tabs for indentation +indent-style = "tab" + +# Format code blocks within docstrings +docstring-code-format = true diff --git a/PACKAGE_TEMPLATE/{{_copier_conf.answers_file}}.jinja b/PACKAGE_TEMPLATE/config/{{_copier_conf.answers_file}}.jinja similarity index 100% rename from PACKAGE_TEMPLATE/{{_copier_conf.answers_file}}.jinja rename to PACKAGE_TEMPLATE/config/{{_copier_conf.answers_file}}.jinja diff --git a/PACKAGE_TEMPLATE/hatch.toml.jinja b/PACKAGE_TEMPLATE/hatch.toml.jinja deleted file mode 100644 index e3bbece..0000000 --- a/PACKAGE_TEMPLATE/hatch.toml.jinja +++ /dev/null @@ -1,9 +0,0 @@ - -[version] -path = "src/{{ package_name }}/__init__.py" - -[build] -directory = "dist" - -[build.targets] -wheel = { packages = ["src/{{ package_name }}"] } diff --git a/PACKAGE_TEMPLATE/pixi.toml.jinja b/PACKAGE_TEMPLATE/pixi.toml.jinja new file mode 100644 index 0000000..f7e7674 --- /dev/null +++ b/PACKAGE_TEMPLATE/pixi.toml.jinja @@ -0,0 +1,143 @@ +#--------------------------------------------------------------------------------------------------# +############################################## PIXI ################################################ +#__________________________________________________________________________________________________# +[project] +name = "{{ package_name }}" +channels = ["conda-forge"] +platforms = ["osx-arm64", "linux-64", "win-64", "osx-64"] + +[environments] +dev = { features = ["dev", "test", "quality", "docs"], solve-group = "default" } +quality = { features = ["quality"], solve-group = "default" } +docs = { features = ["docs"], solve-group = "default" } +publish = { features = ["build", "release"], solve-group = "default" } +{%- if python_version <= "3.10" %} +py310 = { features = ["test", "py310"] } +{%- endif %} +{%- if python_version <= "3.11" %} +py311 = { features = ["test", "py311"] } +{%- endif %} +{%- if python_version <= "3.12" %} +py312 = { features = ["test", "py312"] } +{%- endif %} +{%- if python_version <= "3.13" %} +py313 = { features = ["test", "py313"] } +{%- endif %} + + +############################################# DEFAULT ############################################# +[pypi-dependencies] +{{ package_name }} = { path = ".", editable = true } + +[dependencies] +python = ">={{ python_version }}" + +####################################### PYTHON VERSIONS ########################################### +{% if python_version <= "3.10" %}[feature.py310.dependencies] +python = "3.10.*" +{% endif %}{% if python_version <= "3.11" %}[feature.py311.dependencies] +python = "3.11.*" +{% endif %}{% if python_version <= "3.12" %}[feature.py312.dependencies] +python = "3.12.*" +{% endif %}{% if python_version <= "3.13" %}[feature.py313.dependencies] +python = "3.13.*" +{% endif %} + +############################################# DEV ################################################## +[feature.dev.dependencies] +ipython = "*" +ipykernel = "*" +jupyterlab = "*" +pip = "*" + + +############################################## TEST ################################################ +[feature.test.dependencies] +pytest = "*" +pytest-cov = "*" +pytest-xdist = "*" + +[feature.test.tasks.test] +cmd = "pytest -c config/pytest.ini" +inputs = ["src", "tests", "config/pytest.ini", "config/coverage.toml"] +outputs = ["coverage-report/coverage.xml"] +description = "Run pytest" + +[feature.test.tasks.coverage] +cmd = "coverage report --rcfile=config/coverage.toml" +inputs = ["src", "tests", "coverage-report/coverage.xml"] +description = "Generate generated coverage report" +depends-on = ["test"] + +############################################## QUALITY ############################################### +# Quality includes linting, type checking, and formatting +[feature.quality.dependencies] +ruff = ">=0.4.4" +mypy = ">=1.13.0,<2" + +[feature.quality.pypi-dependencies] +pydoctest = ">=0.2.1, <0.3" + +[feature.quality.tasks] +ruff-check.cmd = ["ruff", "--config", "config/ruff.toml", "check", "src"] +ruff-check.inputs = ["config/ruff.toml", "src"] +ruff-check.description = "Run ruff check" + +ruff-format.cmd = ["ruff", "--config", "config/ruff.toml", "format", "src"] +ruff-format.inputs = ["config/ruff.toml", "src"] +ruff-format.depends_on = ["ruff-check"] +ruff-format.description = "Run ruff format, run check first" + +type-check.cmd = ["mypy", "--config-file", "config/mypy.ini"] +type-check.inputs = ["config/mypy.ini", "src"] +type-check.description = "Run mypy type check." + +qc.depends_on = ["ruff-format", "ruff-check", "type-check"] +qc.description = "Quality check: ruff & mypy" + +############################################## DOCS ################################################ +# Available tasks: doc-build, doc-serve, doc-deploy + +[feature.docs.dependencies] +mkdocs = "*" + +[feature.docs.tasks.doc-build] +cmd = "mkdocs build -f mkdocs.yaml" +inputs = ["docs"] +outputs = ["site"] + +[feature.docs.tasks.doc-serve] +cmd = "mkdocs serve -f mkdocs.yaml" +inputs = ["docs"] + +[feature.docs.tasks.doc-deploy] +cmd = "mkdocs gh-deploy -f mkdocs.yaml --force" +inputs = ["docs"] + +#################################### RELEASE & BUILD ############################################### +[feature.release.dependencies] +python-semantic-release = ">=8.5.1" + +[feature.release.tasks] +semver = 'echo "Next Version is: $(semantic-release version --print)"' + +[feature.build.dependencies] +hatch = "*" + +[feature.build.tasks] +# Builds the package and publishes it to the test.pypi.org repository +build = { cmd = "hatch build --clean", inputs = ["src"], outputs = ["dist/*"] } + +publish-pypi = { cmd = "hatch publish --yes --repo main", inputs = [ + "dist/*", + "hatch.toml", +], depends-on = [ + "build", +] } + +publish-test = { cmd = "hatch publish --yes --repo test", inputs = [ + "dist/*", + "hatch.toml", +], depends-on = [ + "build", +] } diff --git a/PACKAGE_TEMPLATE/pyproject.toml.jinja b/PACKAGE_TEMPLATE/pyproject.toml.jinja index 21b11f8..ce10f24 100644 --- a/PACKAGE_TEMPLATE/pyproject.toml.jinja +++ b/PACKAGE_TEMPLATE/pyproject.toml.jinja @@ -5,16 +5,36 @@ name = "{{ package_name }}" version = "0.1.0" description = "{{ package_description }}" -license = "{% include 'includes/template-license.jinja' %}" +license = {% include 'includes/template-license.jinja' %} readme = "README.md" keywords = ["{{ project_slug }}", "pixi", "python"] authors = [{ name = "{{ author_full_name }}", email = "{{ author_email }}" }] maintainers = [{ name = "{{ author_full_name }}", email = "{{ author_email }}" }] +classifiers = [ + "Development Status :: 1 - Planning", + "Programming Language :: Python :: 3", + {%- if python_version <= "3.10" %} + "Programming Language :: Python :: 3.10", + {%- endif %} + {%- if python_version <= "3.11" %} + "Programming Language :: Python :: 3.11", + {%- endif %} + {%- if python_version <= "3.12" %} + "Programming Language :: Python :: 3.12", + {%- endif %} + {%- if python_version <= "3.13" %} + "Programming Language :: Python :: 3.13", + {%- endif %} + "Operating System :: OS Independent", + "License :: OSI Approved :: {{ software_license }}", +] requires-python = ">= {{ python_version }}" -dependencies = [] +dependencies = [ + "rich", +] [project.urls] homepage = "https://github.com/{{ github_username }}/{{ project_slug }}" @@ -23,99 +43,8 @@ documentation = "https://{{ github_username }}.github.io/{{ project_slug }}/" changelog = "https://github.com/{{ github_username }}/{{ project_slug }}/blob/main/docs/CHANGELOG.md" issues = "https://github.com/{{ github_username }}/{{ project_slug }}/issues" -[project.scripts] -myscript = "{{ package_name }}.main:main" - [build-system] {% include 'includes/template-build-backend.jinja' %} -#--------------------------------------------------------------------------------------------------# -############################################## PIXI ################################################ -#__________________________________________________________________________________________________# -[tool.pixi.project] -channels = ["conda-forge"] -platforms = ["osx-arm64", "linux-64", "win-64", "osx-64"] - -[tool.pixi.pypi-dependencies] -{{ package_name }} = { path = ".", editable = true } - - -[tool.pixi.environments] -dev = { features = ["test", "style", "docs"], solve-group = "default" } -publish = { features = ["build", "release"], solve-group = "default" } - - -############################################## TEST ################################################ -[tool.pixi.feature.test.dependencies] -pytest = "*" -pytest-cov = "*" -pytest-xdist = "*" - -[tool.pixi.feature.test.tasks.test] -cmd = [ - "pytest", - "--numprocesses=auto", - "-s", - "--verbose", - "--cov={{ package_name }}", - "--cov-report=xml:coverage-report/coverage.xml", -] -inputs = ["src", "tests"] -outputs = ["coverage-report/coverage.xml"] - -############################################## STYLE ############################################### -# See ruff.toml for the configuration -[tool.pixi.feature.style.dependencies] -ruff = "*" - -[tool.pixi.feature.style.tasks] -style = { cmd = "ruff check src", inputs = ["src"] } - -############################################## DOCS ################################################ -# Available tasks: doc-build, doc-serve, doc-deploy - -[tool.pixi.feature.docs.dependencies] -mkdocs = "*" - -[tool.pixi.feature.docs.tasks.doc-build] -cmd = "mkdocs build -f mkdocs.yaml" -inputs = ["docs"] -outputs = ["site"] - -[tool.pixi.feature.docs.tasks.doc-serve] -cmd = "mkdocs serve -f mkdocs.yaml" -inputs = ["docs"] - -[tool.pixi.feature.docs.tasks.doc-deploy] -cmd = "mkdocs gh-deploy -f mkdocs.yaml --force" -inputs = ["docs"] - -#################################### RELEASE & BUILD ############################################### -[tool.pixi.feature.release.dependencies] -python-semantic-release = ">=8.5.1" - -[tool.pixi.feature.release.tasks] -# Semver task will only work on the main or dev/develop branch (see releaserc.toml:branches) -semver = 'echo "Next Version is: $(semantic-release -c releaserc.toml version --print)"' -release = "semantic-release -c releaserc.toml version" - -[tool.pixi.feature.build.dependencies] -hatch = "*" - -[tool.pixi.feature.build.tasks] -# Builds the package and publishes it to the test.pypi.org repository -build = { cmd = "hatch build --clean", inputs = ["src"], outputs = ["dist/*"] } - -publish-pypi = { cmd = "hatch publish --yes --repo main", inputs = [ - "dist/*", - "hatch.toml", -], depends-on = [ - "build", -] } - -publish-test = { cmd = "hatch publish --yes --repo test", inputs = [ - "dist/*", - "hatch.toml", -], depends-on = [ - "build", -] } +[project.scripts] +myscript = "{{ package_name }}.main:main" diff --git a/PACKAGE_TEMPLATE/ruff.toml.jinja b/PACKAGE_TEMPLATE/ruff.toml.jinja deleted file mode 100644 index e425059..0000000 --- a/PACKAGE_TEMPLATE/ruff.toml.jinja +++ /dev/null @@ -1,44 +0,0 @@ - -target-version = "py39" -output-format = "full" -line-length = 99 -fix = true -# extend-exclude is used to exclude directories from the flake8 checks -extend-exclude = ["docs/*", "tests/*"] - -[lint] -select = [ - "E", - "F", - "W", # flake8 - "C", # mccabe - "I", # isort - "N", # pep8-naming - "D", # flake8-docstrings - "ANN", # flake8-annotations - "S", # flake8-bandit - "BLE", # flake8-blind-except - "B", # flake8-bugbear - "A", # flake8-builtins - "G", # flake8-logging-format - "ERA", # eradicate - "ISC", # flake8-implicit-str-concat - "RUF", # Ruff-specific rules - "TCH", # flake8-type-checking -] -ignore = ["ANN101"] -unfixable = [ - "ERA", # Don't remove commented-out code -] - -[lint.per-file-ignores] -"tests/*" = ["S101"] - -[lint.mccabe] -max-complexity = 10 - -[lint.isort] -known-first-party = ["{{ package_name }}"] - -[lint.pydocstyle] -convention = "google" diff --git a/PACKAGE_TEMPLATE/src/{{package_name}}/__init__.py.jinja b/PACKAGE_TEMPLATE/src/{{package_name}}/__init__.py.jinja index bca71d3..62b60e3 100644 --- a/PACKAGE_TEMPLATE/src/{{package_name}}/__init__.py.jinja +++ b/PACKAGE_TEMPLATE/src/{{package_name}}/__init__.py.jinja @@ -4,6 +4,6 @@ version = "0.1.0" __version__ = version -from .main import main +from {{package_name}}.main import main __all__ = ["main"] diff --git a/PACKAGE_TEMPLATE/src/{{package_name}}/main.py.jinja b/PACKAGE_TEMPLATE/src/{{package_name}}/main.py.jinja index 9019095..e1c6b2e 100644 --- a/PACKAGE_TEMPLATE/src/{{package_name}}/main.py.jinja +++ b/PACKAGE_TEMPLATE/src/{{package_name}}/main.py.jinja @@ -1,4 +1,4 @@ -from rich import print +from rich import print as rprint def hello() -> tuple[str, str]: @@ -8,7 +8,7 @@ def hello() -> tuple[str, str]: def main() -> None: """Print a greeting message and an emoji.""" - print(*hello()) + rprint(*hello()) if __name__ == "__main__": main() diff --git a/PACKAGE_TEMPLATE/tests/test_say_hello.py.jinja b/PACKAGE_TEMPLATE/tests/test_say_hello.py.jinja index 41abbcf..3a946bf 100644 --- a/PACKAGE_TEMPLATE/tests/test_say_hello.py.jinja +++ b/PACKAGE_TEMPLATE/tests/test_say_hello.py.jinja @@ -1,5 +1,5 @@ import pytest -from {{ package_name }}.main import hello +from {{ package_name }}.main import hello, main; def test_say_hello(): @@ -7,6 +7,13 @@ def test_say_hello(): "Hello from {{ package_name }}, [bold magenta]World[/bold magenta]!", ":vampire:" ) +def test_main(capsys): + """Test the main function.""" + main() + captured = capsys.readouterr() + assert "{{ package_name }}" in captured.out + assert "World" in captured.out + assert "🧛" in captured.out if __name__ == "__main__": pytest.main() diff --git a/copier-settings.yml b/copier-settings.yml index e908db0..71b084f 100644 --- a/copier-settings.yml +++ b/copier-settings.yml @@ -11,6 +11,10 @@ _subdirectory: PACKAGE_TEMPLATE _tasks: # {{ pixi_install_envs }} is a list of strings + - >- + {% if git_init %} + echo "Initializing a git repository..." && git init + {% endif %} - >- {% if pixi_install %} {% for env in pixi_install_envs %} @@ -21,10 +25,6 @@ _tasks: {% endif %} {% endfor %} {% endif %} - - >- - {% if git_init %} - echo "Initializing a git repository..." - {% endif %} _message_before_copy: | @@ -42,4 +42,6 @@ _message_after_copy: | $ cd {{ _copier_conf.dst_path }} - 2. Read "CONTRIBUING.md" and start coding. + 2. Enter the `dev` environment: + + $ pixi shell -e dev diff --git a/copier.yml b/copier.yml index d9ab36a..fe0176e 100644 --- a/copier.yml +++ b/copier.yml @@ -51,13 +51,12 @@ package_description: python_version: type: str help: What is the minimum Python version required? - default: "3.12" + default: "3.10" choices: - - "3.8" - - "3.9" - "3.10" - "3.11" - "3.12" + - "3.13" software_license: type: str @@ -91,8 +90,6 @@ build_backend: # - setuptools # - distutils - - pixi_install: type: bool help: Run `pixi install` after copying files? @@ -105,6 +102,8 @@ pixi_install_envs: choices: - default - dev - - release - default : ["default"] + - docs + - quality + - publish + default : ["dev"] when: "{{ pixi_install == true }}" diff --git a/includes/template-license.jinja b/includes/template-license.jinja index 80e1e32..6de00b4 100644 --- a/includes/template-license.jinja +++ b/includes/template-license.jinja @@ -1,66 +1,66 @@ {# Jinja template to generate license name -#} {%- if software_license == "Apache License 2.0"-%} - Apache-2.0 + "Apache-2.0" {%- elif software_license == "Artistic License 2.0" -%} - Artistic-2.0 + "Artistic-2.0" {%- elif software_license == "BSD 2-Clause \"Simplified\" License" -%} - BSD-2-Clause + "BSD-2-Clause" {%- elif software_license == "BSD 3-Clause Clear License" -%} - BSD-3-Clause-Clear + "BSD-3-Clause-Clear" {%- elif software_license == "BSD 3-Clause \"New\" or \"Revised\" License" -%} - BSD-3-Clause + "BSD-3-Clause" {%- elif software_license == "Boost Software License 1.0" -%} - BSL-1.0 + "BSL-1.0" {%- elif software_license == "Creative Commons Attribution 4.0" -%} - CC-BY-4.0 + "CC-BY-4.0" {%- elif software_license == "Creative Commons Attribution Share Alike 4.0" -%} - CC-BY-SA-4.0 + "CC-BY-SA-4.0" {%- elif software_license == "Creative Commons Zero v1.0 Universal" -%} - CC0-1.0 + "CC0-1.0" {%- elif software_license == "Do What The F*ck You Want To Public License" -%} - WTFPL + "WTFPL" {%- elif software_license == "Educational Community License v2.0" -%} - ECL-2.0 + "ECL-2.0" {%- elif software_license == "Eclipse Public License 1.0" -%} - EPL-1.0 + "EPL-1.0" {%- elif software_license == "Eclipse Public License 2.0" -%} - EPL-2.0 + "EPL-2.0" {%- elif software_license == "European Union Public License 1.1" -%} - EUPL-1.1 + "EUPL-1.1" {%- elif software_license == "European Union Public License 1.2" -%} - EUPL-1.2 + "EUPL-1.2" {%- elif software_license == "GNU Affero General Public License v3.0" -%} - AGPL-3.0 + "AGPL-3.0" {%- elif software_license == "GNU General Public License v2.0"-%} - GPL-2.0 + "GPL-2.0" {%- elif software_license == "GNU General Public License v3.0"-%} - GPL-3.0 + "GPL-3.0" {%- elif software_license == "GNU Lesser General Public License v2.1" -%} - LGPL-2.1 + "LGPL-2.1" {%- elif software_license == "GNU Lesser General Public License v3.0" -%} - LGPL-3.0 + "LGPL-3.0" {%- elif software_license == "ISC License" -%} - ISC + "ISC" {%- elif software_license == "LaTeX Project Public License v1.3c" -%} - LPPL-1.3c + "LPPL-1.3c" {%- elif software_license == "MIT License"-%} - MIT + "MIT" {%- elif software_license == "Mozilla Public License 2.0" -%} - MPL-2.0 + "MPL-2.0" {%- elif software_license == "Microsoft Public License" -%} - MS-PL + "MS-PL" {%- elif software_license == "Microsoft Reciprocal License" -%} - MS-RL + "MS-RL" {%- elif software_license == "University of Illinois/NCSA Open Source License" -%} - NCSA + "NCSA" {%- elif software_license == "SIL Open Font License 1.1" -%} - OFL-1.1 + "OFL-1.1" {%- elif software_license == "Open Software License 3.0" -%} - OSL-3.0 + "OSL-3.0" {%- elif software_license == "PostgreSQL License" -%} - PostgreSQL + "PostgreSQL" {%- elif software_license == "The Unlicense" -%} - Unlicense + "Unlicense" {%- elif software_license == "zlib License" -%} - Zlib + "Zlib" {%- endif %}