diff --git a/.bandit.yml b/.bandit.yml deleted file mode 100644 index fac45de..0000000 --- a/.bandit.yml +++ /dev/null @@ -1,404 +0,0 @@ - -### This config may optionally select a subset of tests to run or skip by -### filling out the 'tests' and 'skips' lists given below. If no tests are -### specified for inclusion then it is assumed all tests are desired. The skips -### set will remove specific tests from the include set. This can be controlled -### using the -t/-s CLI options. Note that the same test ID should not appear -### in both 'tests' and 'skips', this would be nonsensical and is detected by -### Bandit at runtime. - -# Available tests: -# B101 : assert_used -# B102 : exec_used -# B103 : set_bad_file_permissions -# B104 : hardcoded_bind_all_interfaces -# B105 : hardcoded_password_string -# B106 : hardcoded_password_funcarg -# B107 : hardcoded_password_default -# B108 : hardcoded_tmp_directory -# B110 : try_except_pass -# B112 : try_except_continue -# B113 : request_without_timeout -# B201 : flask_debug_true -# B202 : tarfile_unsafe_members -# B301 : pickle -# B302 : marshal -# B303 : md5 -# B304 : ciphers -# B305 : cipher_modes -# B306 : mktemp_q -# B307 : eval -# B308 : mark_safe -# B310 : urllib_urlopen -# B311 : random -# B312 : telnetlib -# B313 : xml_bad_cElementTree -# B314 : xml_bad_ElementTree -# B315 : xml_bad_expatreader -# B316 : xml_bad_expatbuilder -# B317 : xml_bad_sax -# B318 : xml_bad_minidom -# B319 : xml_bad_pulldom -# B320 : xml_bad_etree -# B321 : ftplib -# B323 : unverified_context -# B324 : hashlib_insecure_functions -# B401 : import_telnetlib -# B402 : import_ftplib -# B403 : import_pickle -# B404 : import_subprocess -# B405 : import_xml_etree -# B406 : import_xml_sax -# B407 : import_xml_expat -# B408 : import_xml_minidom -# B409 : import_xml_pulldom -# B410 : import_lxml -# B411 : import_xmlrpclib -# B412 : import_httpoxy -# B413 : import_pycrypto -# B415 : import_pyghmi -# B501 : request_with_no_cert_validation -# B502 : ssl_with_bad_version -# B503 : ssl_with_bad_defaults -# B504 : ssl_with_no_version -# B505 : weak_cryptographic_key -# B506 : yaml_load -# B507 : ssh_no_host_key_verification -# B508 : snmp_insecure_version -# B509 : snmp_weak_cryptography -# B601 : paramiko_calls -# B602 : subprocess_popen_with_shell_equals_true -# B603 : subprocess_without_shell_equals_true -# B604 : any_other_function_with_shell_equals_true -# B605 : start_process_with_a_shell -# B606 : start_process_with_no_shell -# B607 : start_process_with_partial_path -# B608 : hardcoded_sql_expressions -# B609 : linux_commands_wildcard_injection -# B610 : django_extra_used -# B611 : django_rawsql_used -# B612 : logging_config_insecure_listen -# B701 : jinja2_autoescape_false -# B702 : use_of_mako_templates -# B703 : django_mark_safe - -# (optional) list included test IDs here, eg '[B101, B406]': -tests: - -# (optional) list skipped test IDs here, eg '[B101, B406]': -skips: - -### (optional) plugin settings - some test plugins require configuration data -### that may be given here, per-plugin. All bandit test plugins have a built in -### set of sensible defaults and these will be used if no configuration is -### provided. It is not necessary to provide settings for every (or any) plugin -### if the defaults are acceptable. - -any_other_function_with_shell_equals_true: - no_shell: - - os.execl - - os.execle - - os.execlp - - os.execlpe - - os.execv - - os.execve - - os.execvp - - os.execvpe - - os.spawnl - - os.spawnle - - os.spawnlp - - os.spawnlpe - - os.spawnv - - os.spawnve - - os.spawnvp - - os.spawnvpe - - os.startfile - shell: - - os.system - - os.popen - - os.popen2 - - os.popen3 - - os.popen4 - - popen2.popen2 - - popen2.popen3 - - popen2.popen4 - - popen2.Popen3 - - popen2.Popen4 - - commands.getoutput - - commands.getstatusoutput - subprocess: - - subprocess.Popen - - subprocess.call - - subprocess.check_call - - subprocess.check_output - - subprocess.run -assert_used: - skips: - - tests/*.py - - ./tests/*.py -hardcoded_tmp_directory: - tmp_dirs: - - /tmp - - /var/tmp - - /dev/shm -linux_commands_wildcard_injection: - no_shell: - - os.execl - - os.execle - - os.execlp - - os.execlpe - - os.execv - - os.execve - - os.execvp - - os.execvpe - - os.spawnl - - os.spawnle - - os.spawnlp - - os.spawnlpe - - os.spawnv - - os.spawnve - - os.spawnvp - - os.spawnvpe - - os.startfile - shell: - - os.system - - os.popen - - os.popen2 - - os.popen3 - - os.popen4 - - popen2.popen2 - - popen2.popen3 - - popen2.popen4 - - popen2.Popen3 - - popen2.Popen4 - - commands.getoutput - - commands.getstatusoutput - subprocess: - - subprocess.Popen - - subprocess.call - - subprocess.check_call - - subprocess.check_output - - subprocess.run -ssl_with_bad_defaults: - bad_protocol_versions: - - PROTOCOL_SSLv2 - - SSLv2_METHOD - - SSLv23_METHOD - - PROTOCOL_SSLv3 - - PROTOCOL_TLSv1 - - SSLv3_METHOD - - TLSv1_METHOD - - PROTOCOL_TLSv1_1 - - TLSv1_1_METHOD -ssl_with_bad_version: - bad_protocol_versions: - - PROTOCOL_SSLv2 - - SSLv2_METHOD - - SSLv23_METHOD - - PROTOCOL_SSLv3 - - PROTOCOL_TLSv1 - - SSLv3_METHOD - - TLSv1_METHOD - - PROTOCOL_TLSv1_1 - - TLSv1_1_METHOD -start_process_with_a_shell: - no_shell: - - os.execl - - os.execle - - os.execlp - - os.execlpe - - os.execv - - os.execve - - os.execvp - - os.execvpe - - os.spawnl - - os.spawnle - - os.spawnlp - - os.spawnlpe - - os.spawnv - - os.spawnve - - os.spawnvp - - os.spawnvpe - - os.startfile - shell: - - os.system - - os.popen - - os.popen2 - - os.popen3 - - os.popen4 - - popen2.popen2 - - popen2.popen3 - - popen2.popen4 - - popen2.Popen3 - - popen2.Popen4 - - commands.getoutput - - commands.getstatusoutput - subprocess: - - subprocess.Popen - - subprocess.call - - subprocess.check_call - - subprocess.check_output - - subprocess.run -start_process_with_no_shell: - no_shell: - - os.execl - - os.execle - - os.execlp - - os.execlpe - - os.execv - - os.execve - - os.execvp - - os.execvpe - - os.spawnl - - os.spawnle - - os.spawnlp - - os.spawnlpe - - os.spawnv - - os.spawnve - - os.spawnvp - - os.spawnvpe - - os.startfile - shell: - - os.system - - os.popen - - os.popen2 - - os.popen3 - - os.popen4 - - popen2.popen2 - - popen2.popen3 - - popen2.popen4 - - popen2.Popen3 - - popen2.Popen4 - - commands.getoutput - - commands.getstatusoutput - subprocess: - - subprocess.Popen - - subprocess.call - - subprocess.check_call - - subprocess.check_output - - subprocess.run -start_process_with_partial_path: - no_shell: - - os.execl - - os.execle - - os.execlp - - os.execlpe - - os.execv - - os.execve - - os.execvp - - os.execvpe - - os.spawnl - - os.spawnle - - os.spawnlp - - os.spawnlpe - - os.spawnv - - os.spawnve - - os.spawnvp - - os.spawnvpe - - os.startfile - shell: - - os.system - - os.popen - - os.popen2 - - os.popen3 - - os.popen4 - - popen2.popen2 - - popen2.popen3 - - popen2.popen4 - - popen2.Popen3 - - popen2.Popen4 - - commands.getoutput - - commands.getstatusoutput - subprocess: - - subprocess.Popen - - subprocess.call - - subprocess.check_call - - subprocess.check_output - - subprocess.run -subprocess_popen_with_shell_equals_true: - no_shell: - - os.execl - - os.execle - - os.execlp - - os.execlpe - - os.execv - - os.execve - - os.execvp - - os.execvpe - - os.spawnl - - os.spawnle - - os.spawnlp - - os.spawnlpe - - os.spawnv - - os.spawnve - - os.spawnvp - - os.spawnvpe - - os.startfile - shell: - - os.system - - os.popen - - os.popen2 - - os.popen3 - - os.popen4 - - popen2.popen2 - - popen2.popen3 - - popen2.popen4 - - popen2.Popen3 - - popen2.Popen4 - - commands.getoutput - - commands.getstatusoutput - subprocess: - - subprocess.Popen - - subprocess.call - - subprocess.check_call - - subprocess.check_output - - subprocess.run -subprocess_without_shell_equals_true: - no_shell: - - os.execl - - os.execle - - os.execlp - - os.execlpe - - os.execv - - os.execve - - os.execvp - - os.execvpe - - os.spawnl - - os.spawnle - - os.spawnlp - - os.spawnlpe - - os.spawnv - - os.spawnve - - os.spawnvp - - os.spawnvpe - - os.startfile - shell: - - os.system - - os.popen - - os.popen2 - - os.popen3 - - os.popen4 - - popen2.popen2 - - popen2.popen3 - - popen2.popen4 - - popen2.Popen3 - - popen2.Popen4 - - commands.getoutput - - commands.getstatusoutput - subprocess: - - subprocess.Popen - - subprocess.call - - subprocess.check_call - - subprocess.check_output - - subprocess.run -try_except_continue: - check_typed_exception: false -try_except_pass: - check_typed_exception: false -weak_cryptographic_key: - weak_key_size_dsa_high: 1024 - weak_key_size_dsa_medium: 2048 - weak_key_size_ec_high: 160 - weak_key_size_ec_medium: 224 - weak_key_size_rsa_high: 1024 - weak_key_size_rsa_medium: 2048 diff --git a/.codeclimate.yml b/.codeclimate.yml index 219217b..a68c916 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,12 +1,4 @@ version: "2" plugins: - bandit: - enabled: true - duplication: - enabled: true - config: - languages: - python: - python_version: 3 sonar-python: enabled: true diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..39a6cb8 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @febus982 \ No newline at end of file diff --git a/.github/workflows/python-code-style.yml b/.github/workflows/python-code-style.yml index 888faed..39769a7 100644 --- a/.github/workflows/python-code-style.yml +++ b/.github/workflows/python-code-style.yml @@ -12,21 +12,20 @@ on: workflow_dispatch: jobs: - quality: + format: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up Python 3.12 + - name: Set up Python 3.13 uses: actions/setup-python@v5 with: - python-version: "3.12" + python-version: "3.13" - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install poetry - poetry config virtualenvs.create false - poetry install --no-root --with dev + python -m pip install poetry tox + make poetry-export - name: Check code style with black run: | make format diff --git a/.github/workflows/python-lint.yml b/.github/workflows/python-lint.yml index 30a6d48..a00e294 100644 --- a/.github/workflows/python-lint.yml +++ b/.github/workflows/python-lint.yml @@ -12,20 +12,19 @@ on: workflow_dispatch: jobs: - quality: + lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up Python 3.12 + - name: Set up Python 3.13 uses: actions/setup-python@v5 with: - python-version: "3.12" + python-version: "3.13" - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install poetry - poetry config virtualenvs.create false - poetry install --no-root --with dev + python -m pip install poetry tox + make poetry-export - name: Lint with ruff run: make lint diff --git a/.github/workflows/python-quality.yml b/.github/workflows/python-quality.yml index 171a92d..fcc57e4 100644 --- a/.github/workflows/python-quality.yml +++ b/.github/workflows/python-quality.yml @@ -17,10 +17,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Python 3.12 + - name: Set up Python 3.13 uses: actions/setup-python@v5 with: - python-version: "3.12" + python-version: "3.13" - name: Install dependencies run: | python -m pip install --upgrade pip @@ -28,7 +28,7 @@ jobs: poetry config virtualenvs.create false poetry install --no-root --with dev - name: Test & publish code coverage - uses: paambaati/codeclimate-action@v5.0.0 + uses: paambaati/codeclimate-action@v9.0.0 env: CC_TEST_REPORTER_ID: ${{ secrets.CODECLIMATE_REPORTER_ID }} with: diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 0aa4db3..8335564 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -8,14 +8,18 @@ on: branches: [ "main" ] pull_request: branches: [ "main" ] + # Run tests on Friday to check if tests pass with updated dependencies + schedule: + - cron: '0 0 * * 5' # Allows you to run this workflow manually from the Actions tab workflow_dispatch: jobs: test: strategy: + fail-fast: false matrix: - version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + version: ["3.9", "3.10", "3.11", "3.12", "3.13"] os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: @@ -31,8 +35,49 @@ jobs: poetry config virtualenvs.create false poetry install --no-root --with dev - name: Test with pytest + id: citest run: | make ci-test - - name: Check typing - run: | - make typing + + failure-notification: + runs-on: ubuntu-latest + needs: test + if: failure() && github.event.schedule == '0 0 * * 5' + permissions: + issues: write + steps: + - uses: actions/checkout@v4 + - name: Create label if not exists + run: | + gh label create scheduled-failure --force --color B60205 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Opens an issue if not already existing and open + run: | + previous_issue_number=$(gh issue list \ + --label "$LABELS" \ + --json number \ + --jq '.[0].number') + if [[ -n $previous_issue_number ]]; then + gh issue edit "$previous_issue_number" --body "$BODY" + else + new_issue_url=$(gh issue create \ + --title "$TITLE" \ + --label "$LABELS" \ + --body "$BODY") + if [[ $PINNED == true ]]; then + gh issue pin "$new_issue_url" + fi + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + TITLE: Scheduled automated test failure + LABELS: scheduled-failure + BODY: | + ### Test suite failed during scheduled run + + [Link to failing run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) + + PINNED: false diff --git a/.github/workflows/python-bandit.yml b/.github/workflows/python-typing.yml similarity index 56% rename from .github/workflows/python-bandit.yml rename to .github/workflows/python-typing.yml index 50a6991..d92e04e 100644 --- a/.github/workflows/python-bandit.yml +++ b/.github/workflows/python-typing.yml @@ -1,7 +1,7 @@ # This workflow will install Python dependencies, run tests and lint with a variety of Python versions # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python -name: Bandit checks +name: Python typing on: push: @@ -12,21 +12,19 @@ on: workflow_dispatch: jobs: - bandit: + typing: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - - name: Security check - Bandit - uses: ioggstream/bandit-report-artifacts@v1.7.4 - with: - project_path: . - config_file: .bandit.yml - - # This is optional - - name: Security check report artifacts - uses: actions/upload-artifact@v4 + - name: Set up Python 3.13 + uses: actions/setup-python@v5 with: - name: Security report - path: output/security_report.txt + python-version: "3.13" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install poetry tox + make poetry-export + - name: Check typing + run: make typing diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d217030..2812e19 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,10 +17,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Set up Python 3.12 + - name: Set up Python 3.13 uses: actions/setup-python@v5 with: - python-version: "3.12" + python-version: "3.13" - name: Install dependencies run: | diff --git a/.github/workflows/reusable-github-pages.yml b/.github/workflows/reusable-github-pages.yml index 17231d4..26794b3 100644 --- a/.github/workflows/reusable-github-pages.yml +++ b/.github/workflows/reusable-github-pages.yml @@ -39,17 +39,19 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Set up Python 3.12 + - name: Set up Python 3.13 uses: actions/setup-python@v5 with: - python-version: "3.12" + python-version: "3.13" + # Here we want to install the current package in editable mode, + # in case mkdocs needs the package (i.e. we are building a mkdocs plugin). - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install poetry poetry config virtualenvs.create false - poetry install --no-root --with dev + poetry install --with dev - name: Configure Git user run: | diff --git a/.idea/bootstrap-python-package.iml b/.idea/bootstrap-python-package.iml index c81a831..53b24a8 100644 --- a/.idea/bootstrap-python-package.iml +++ b/.idea/bootstrap-python-package.iml @@ -3,8 +3,9 @@ <component name="NewModuleRootManager"> <content url="file://$MODULE_DIR$"> <sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/.tox" /> </content> <orderEntry type="jdk" jdkName="Poetry (bootstrap-python-package)" jdkType="Python SDK" /> <orderEntry type="sourceFolder" forTests="false" /> </component> -</module> +</module> \ No newline at end of file diff --git a/.idea/copyright/MIT.xml b/.idea/copyright/MIT.xml new file mode 100644 index 0000000..383dfdd --- /dev/null +++ b/.idea/copyright/MIT.xml @@ -0,0 +1,6 @@ +<component name="CopyrightManager"> + <copyright> + <option name="notice" value="Copyright (c) &#36;originalComment.match("Copyright \(c\) (\d+)", 1, "-", "&#36;today.year")&#36;today.year Federico Busetti <729029+febus982@users.noreply.github.com> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." /> + <option name="myName" value="MIT" /> + </copyright> +</component> \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..50778f8 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,23 @@ +<component name="CopyrightManager"> + <settings default="MIT"> + <module2copyright> + <element module="All" copyright="MIT" /> + </module2copyright> + <LanguageOptions name="Python"> + <option name="relativeBefore" value="false" /> + <option name="block" value="false" /> + <option name="separateBefore" value="true" /> + <option name="separateAfter" value="true" /> + <option name="box" value="true" /> + <option name="filler" value="=" /> + </LanguageOptions> + <LanguageOptions name="__TEMPLATE__"> + <option name="block" value="false" /> + <option name="separateBefore" value="true" /> + <option name="separateAfter" value="true" /> + <option name="prefixLines" value="false" /> + <option name="box" value="true" /> + <option name="filler" value="=" /> + </LanguageOptions> + </settings> +</component> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index cca12c3..692a896 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="Black"> + <option name="executionMode" value="BINARY" /> <option name="sdkName" value="Poetry (sqlalchemy-bind-manager)" /> </component> <component name="ProjectRootManager" version="2" project-jdk-name="Poetry (bootstrap-python-package)" project-jdk-type="Python SDK" /> -</project> +</project> \ No newline at end of file diff --git a/.idea/runConfigurations/Tox.xml b/.idea/runConfigurations/Tox.xml new file mode 100644 index 0000000..1514281 --- /dev/null +++ b/.idea/runConfigurations/Tox.xml @@ -0,0 +1,22 @@ +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="Tox" type="Tox" factoryName="Tox"> + <module name="bootstrap-python-package" /> + <option name="ENV_FILES" value="" /> + <option name="INTERPRETER_OPTIONS" value="" /> + <option name="PARENT_ENVS" value="true" /> + <option name="SDK_HOME" value="$USER_HOME$/Library/Caches/pypoetry/virtualenvs/bootstrap-python-package-Co8LXxYn-py3.12/bin/python" /> + <option name="SDK_NAME" value="Poetry (bootstrap-python-package)" /> + <option name="WORKING_DIRECTORY" value="" /> + <option name="IS_MODULE_SDK" value="false" /> + <option name="ADD_CONTENT_ROOTS" value="true" /> + <option name="ADD_SOURCE_ROOTS" value="true" /> + <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" /> + <arguments> + <array /> + </arguments> + <runOnlyEnvs> + <array /> + </runOnlyEnvs> + <method v="2" /> + </configuration> +</component> \ No newline at end of file diff --git a/Makefile b/Makefile index 791d4d6..9ee8046 100644 --- a/Makefile +++ b/Makefile @@ -1,40 +1,49 @@ -.PHONY: docs +.PHONY: dev-dependencies update-dependencies test docs fix check typing lint format ci-test ci-coverage poetry-export + +######################### +###### dev commands ##### +######################### +dev-dependencies: + poetry install --with dev --no-root + +update-dependencies: + poetry update --with dev test: poetry run pytest -n auto --cov -ci-test: - poetry run pytest - -ci-coverage: - poetry run pytest --cov --cov-report lcov +docs: + poetry run mkdocs serve -typing: - poetry run mypy +fix: + poetry run ruff check . --fix + poetry run ruff format . -format: - poetry run black --check . +check: poetry-export + tox -lint: - poetry run ruff . +typing: poetry-export + tox -e typing -bandit: - poetry run bandit -c .bandit.yml -r . +lint: poetry-export + tox -e lint -format-fix: - poetry run black . +format: poetry-export + tox -e format -lint-fix: - poetry run ruff . --fix -dev-dependencies: - poetry install --with dev --no-root +######################### +#### Helper commands #### +######################### +poetry-export: + poetry export -f requirements.txt --output /tmp/requirements.txt --with dev -update-dependencies: - poetry update --with dev -fix: format-fix lint-fix -check: typing format lint test bandit +######################### +###### CI commands ###### +######################### +ci-test: + poetry run pytest -docs: - poetry run mkdocs serve +ci-coverage: + poetry run pytest --cov --cov-report lcov diff --git a/README.md b/README.md index e0691dc..14e4e24 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,30 @@ # bootstrap-python-package - + [](https://pypi.org/project/bootstrap-python-package/) [](https://github.com/mkenney/software-guides/blob/master/STABILITY-BADGES.md#beta) [](https://github.com/febus982/bootstrap-python-package/actions/workflows/python-tests.yml) -[](https://github.com/febus982/bootstrap-python-package/actions/workflows/python-bandit.yml) [](https://codeclimate.com/github/febus982/bootstrap-python-package/maintainability) [](https://codeclimate.com/github/febus982/bootstrap-python-package/test_coverage) [](https://mypy-lang.org/) -[](https://github.com/psf/black) [](https://github.com/charliermarsh/ruff) +[](https://github.com/psf/black) [](https://github.com/PyCQA/bandit) This template repository provides the boilerplate to create a python package. It is configured with all the following features: -* Test suite using [pytest](https://docs.pytest.org/en/7.4.x/) +* Test suite using [tox](https://tox.wiki/en/latest/index.html) and [pytest](https://docs.pytest.org/en/7.4.x/) * Typing using [mypy](https://mypy.readthedocs.io/en/stable/) -* Linting using [ruff](https://github.com/astral-sh/ruff) -* Code formatter using [black](https://pypi.org/project/black/) -* Security checks using [bandit](https://github.com/PyCQA/bandit) +* Linting, security and code format using [ruff](https://github.com/astral-sh/ruff) (using [black](https://pypi.org/project/black/) + code style and [bandit](https://github.com/PyCQA/bandit) security rules) * Integration with CodeClimate for code quality and coverage checks * CI pipeline supporting: * testing against multiple python versions * releases on [PyPI](https://pypi.org) * GitHub pages documentation using [mkdocs](https://www.mkdocs.org) - -This project doesn't currently use [tox](https://tox.wiki/en/4.11.4/index.html) or other matrix -testing utilities. I prefer to run the tests only against the latest python locally, and run -previous python versions directly in the CI pipeline. +* PyCharm profile basic configuration ## How to use this repository template to create a new package @@ -37,7 +32,7 @@ previous python versions directly in the CI pipeline. * Rename the `bootstrap_python_package` directory * Search and replace all the occurrences of `bootstrap-python-package` and `bootstrap_python_package` * Configure a pending trusted publisher on [pypi](https://pypi.org/manage/account/publishing) using the following values: - * PyPI Project Name: what you renamed the directory `bootstrap_python_package` to (Double check `_` and `-`) + * PyPI Project Name: The github repository name (in this case `bootstrap-python-package`) * Owner: The github repository owner (in this case `febus982`) * Repository name: The github repository name (in this case `bootstrap-python-package`) * Workflow name: `release.yml` @@ -45,6 +40,7 @@ previous python versions directly in the CI pipeline. containing the codeclimate reporter id (you can find it at `https://codeclimate.com/repos/YOUR_REPO_ID/settings/test_reporter`). If you don't want to use CodeClimate just delete `workflows/python-quality.yml`. * Update the badges in `README.md`! (check [shields.io](https://shields.io/) for extra badges) +* Update the PyCharm Copyright profile in the IDE settings: Editor | Copyright | Copyright Profiles (if you want to use it) * Setup local development: * Clone the repository * Install poetry `pip install poetry` @@ -84,7 +80,8 @@ All the common commands used during development can be run using make targets: * `make dev-dependencies`: Install dev requirements * `make update-dependencies`: Update dev requirements -* `make test`: Run test suite -* `make check`: Run tests, code style and lint checks * `make fix`: Run code style and lint automatic fixes (where possible) +* `make test`: Run test suite against system python version +* `make check`: Run tests against all available python versions, code style and lint checks +* `make type`, `make format`, `make lint`, `make bandit`: Run the relevant check * `make docs`: Render the mkdocs website locally diff --git a/bootstrap_python_package/__init__.py b/bootstrap_python_package/__init__.py index 60b4200..715bbf3 100644 --- a/bootstrap_python_package/__init__.py +++ b/bootstrap_python_package/__init__.py @@ -1,3 +1,30 @@ +# ============================================================================== +# Copyright (c) 2024 Federico Busetti = +# <729029+febus982@users.noreply.github.com> = +# = +# Permission is hereby granted, free of charge, to any person obtaining a = +# copy of this software and associated documentation files (the "Software"), = +# to deal in the Software without restriction, including without limitation = +# the rights to use, copy, modify, merge, publish, distribute, sublicense, = +# and/or sell copies of the Software, and to permit persons to whom the = +# Software is furnished to do so, subject to the following conditions: = +# = +# The above copyright notice and this permission notice shall be included in = +# all copies or substantial portions of the Software. = +# = +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR = +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, = +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL = +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER = +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING = +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER = +# DEALINGS IN THE SOFTWARE. = +# ============================================================================== + +__version__ = "0.0.0" +__version_tuple__ = (0, 0, 0) + + def some_function() -> str: """ Some function docstring @@ -6,7 +33,3 @@ def some_function() -> str: :rtype: str """ return "some_variable_to_test" - - -__version__ = "0.0.0" -__version_tuple__ = (0, 0, 0) diff --git a/docs/.pages b/docs/.pages new file mode 100644 index 0000000..188998a --- /dev/null +++ b/docs/.pages @@ -0,0 +1,4 @@ +nav: + - Home: index.md + - ... + - ADR: adr diff --git a/docs/adr/.markdownlint b/docs/adr/.markdownlint new file mode 100644 index 0000000..52b67b8 --- /dev/null +++ b/docs/adr/.markdownlint @@ -0,0 +1,20 @@ +# source: https://github.com/adr/madr/blob/3.0.0/template/.markdownlint.yml +default: true + +# Allow arbitrary line length +# +# Reason: We apply the one-sentence-per-line rule. A sentence may get longer than 80 characters, especially if links are contained. +# +# Details: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md013---line-length +MD013: false + +# Allow duplicate headings +# +# Reasons: +# +# - The chosen option is considerably often used as title of the ADR (e.g., ADR-0015). Thus, that title repeats. +# - We use "Examples" multiple times (e.g., ADR-0010). +# - Markdown lint should support the user and not annoy them. +# +# Details: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md024---multiple-headings-with-the-same-content +MD024: false \ No newline at end of file diff --git a/docs/adr/.pages b/docs/adr/.pages new file mode 100644 index 0000000..d8c2563 --- /dev/null +++ b/docs/adr/.pages @@ -0,0 +1,3 @@ +nav: + - Summary: summary.md + - ... | regex=^\d{4}- diff --git a/docs/adr/0001-record-architecture-decisions.md b/docs/adr/0001-record-architecture-decisions.md new file mode 100644 index 0000000..f6832e4 --- /dev/null +++ b/docs/adr/0001-record-architecture-decisions.md @@ -0,0 +1,36 @@ +--- +# source: https://github.com/adr/madr/blob/3.0.0/template/adr-template.md +# These are optional elements. Feel free to remove any of them. +status: accepted +date: 2024-02-03 +# status: {proposed | rejected | accepted | deprecated | … | superseded by [ADR-0005](0005-example.md)} +# date: {YYYY-MM-DD when the decision was last updated} +# deciders: {list everyone involved in the decision} +# consulted: {list everyone whose opinions are sought (typically subject-matter experts); and with whom there is a two-way communication} +# informed: {list everyone who is kept up-to-date on progress; and with whom there is a one-way communication} +--- +# Use Markdown Any Decision Records V3 + +## Context and Problem Statement + +We want to record any decisions made in this project independent whether decisions concern the architecture ("architectural decision record"), the code, or other fields. +Which format and structure should these records follow? + +## Considered Options + +* [MADR](https://adr.github.io/madr/) 3.0.0 – The Markdown Any Decision Records +* [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" +* Other templates listed at <https://schubmat.github.io/DecisionCapture> +* Formless – No conventions for file format and structure + +## Decision Outcome + +Chosen option: "MADR 3.0.0", because + +* Implicit assumptions should be made explicit. + Design documentation is important to enable people understanding the decisions later on. + See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). +* MADR allows for structured capturing of any decision. +* The MADR format is lean and fits our development style. +* The MADR structure is comprehensible and facilitates usage & maintenance. +* The MADR project is vivid. diff --git a/docs/adr/adr-template.md b/docs/adr/adr-template.md new file mode 100644 index 0000000..8c12b89 --- /dev/null +++ b/docs/adr/adr-template.md @@ -0,0 +1,80 @@ +--- +# source: https://github.com/adr/madr/blob/3.0.0/template/adr-template.md +# These are optional elements. Feel free to remove any of them. +status: {proposed | rejected | accepted | deprecated | … | superseded by [ADR-0005](0005-example.md)} +date: {YYYY-MM-DD when the decision was last updated} +deciders: {list everyone involved in the decision} +consulted: {list everyone whose opinions are sought (typically subject-matter experts); and with whom there is a two-way communication} +informed: {list everyone who is kept up-to-date on progress; and with whom there is a one-way communication} +--- +# {short title of solved problem and solution} + +## Context and Problem Statement + +{Describe the context and problem statement, e.g., in free form using two to three sentences or in the form of an illustrative story. + You may want to articulate the problem in form of a question and add links to collaboration boards or issue management systems.} + +<!-- This is an optional element. Feel free to remove. --> +## Decision Drivers + +* {decision driver 1, e.g., a force, facing concern, …} +* {decision driver 2, e.g., a force, facing concern, …} +* … <!-- numbers of drivers can vary --> + +## Considered Options + +* {title of option 1} +* {title of option 2} +* {title of option 3} +* … <!-- numbers of options can vary --> + +## Decision Outcome + +Chosen option: "{title of option 1}", because +{justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force {force} | … | comes out best (see below)}. + +<!-- This is an optional element. Feel free to remove. --> +### Consequences + +* Good, because {positive consequence, e.g., improvement of one or more desired qualities, …} +* Bad, because {negative consequence, e.g., compromising one or more desired qualities, …} +* … <!-- numbers of consequences can vary --> + +<!-- This is an optional element. Feel free to remove. --> +## Validation + +{describe how the implementation of/compliance with the ADR is validated. E.g., by a review or an ArchUnit test} + +<!-- This is an optional element. Feel free to remove. --> +## Pros and Cons of the Options + +### {title of option 1} + +<!-- This is an optional element. Feel free to remove. --> +{example | description | pointer to more information | …} + +* Good, because {argument a} +* Good, because {argument b} +<!-- use "neutral" if the given argument weights neither for good nor bad --> +* Neutral, because {argument c} +* Bad, because {argument d} +* … <!-- numbers of pros and cons can vary --> + +### {title of other option} + +{example | description | pointer to more information | …} + +* Good, because {argument a} +* Good, because {argument b} +* Neutral, because {argument c} +* Bad, because {argument d} +* … + +<!-- This is an optional element. Feel free to remove. --> +## More Information + +{You might want to provide additional evidence/confidence for the decision outcome here and/or + document the team agreement on the decision and/or + define when this decision when and how the decision should be realized and if/when it should be re-visited and/or + how the decision is validated. + Links to other decisions and resources might here appear as well.} diff --git a/docs/adr/summary.md b/docs/adr/summary.md new file mode 100644 index 0000000..1fa58ac --- /dev/null +++ b/docs/adr/summary.md @@ -0,0 +1,3 @@ +# ADR Summary + +{{ adr_summary(adr_path="docs/adr", adr_style="MADR3") }} diff --git a/docs/index.md b/docs/index.md index ac0f2df..f8c4caf 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,77 +1 @@ -# bootstrap-python-package - -This template repository provides the boilerplate to create a python package. -It is configured with all the following features: - -* Test suite using [pytest](https://docs.pytest.org/en/7.4.x/) -* Typing using [mypy](https://mypy.readthedocs.io/en/stable/) -* Linting using [ruff](https://github.com/astral-sh/ruff) -* Code formatter using [black](https://pypi.org/project/black/) -* Security checks using [bandit](https://github.com/PyCQA/bandit) -* Integration with CodeClimate for code quality and coverage checks -* CI pipeline supporting: - * testing against multiple python versions - * releases on [PyPI](https://pypi.org) - * GitHub pages documentation using [mkdocs](https://www.mkdocs.org) - -This project doesn't currently use [tox](https://tox.wiki/en/4.11.4/index.html) or other matrix -testing utilities. I prefer to run the tests only against the latest python locally, and run -previous python versions directly in the CI pipeline. - -## How to use this repository template to create a new package - -* Create your github repository using this template. (The big green `Use this template` button) -* Rename the `bootstrap_python_package` directory -* Search and replace all the occurrences of `bootstrap-python-package` and `bootstrap_python_package` -* Configure a pending trusted publisher on [pypi](https://pypi.org/manage/account/publishing) using the following values: - * PyPI Project Name: what you renamed the directory `bootstrap_python_package` to (Double check `_` and `-`) - * Owner: The github repository owner (in this case `febus982`) - * Repository name: The github repository name (in this case `bootstrap-python-package`) - * Workflow name: `release.yml` -* Create a GitHub Actions secret named `CODECLIMATE_REPORTER_ID` (at URL `https://github.com/GITHUB_NAME_OR_ORGANIZATION/GITHUB_REPOSITORY/settings/secrets/actions`) - containing the codeclimate reporter id (you can find it at `https://codeclimate.com/repos/YOUR_REPO_ID/settings/test_reporter`). - If you don't want to use CodeClimate just delete `workflows/python-quality.yml`. -* Update the badges in `README.md`! (check [shields.io](https://shields.io/) for extra badges) -* Setup local development: - * Clone the repository - * Install poetry `pip install poetry` - * Install dev dependencies with `make dev-dependencies` - * (optional) It is strongly recommended to install [pre-commit](https://pre-commit.com/#installation) - and run `pre-commit install` so that formatting and linting are automatically executed during `git commit`. -* Setup GitHub pages (this need local development setup): - * Initialise documentation branch `poetry run mike deploy dev latest --update-aliases --push` - * Configure GitHub Pages to deploy from the `gh-pages` branch (at URL `https://github.com/GITHUB_NAME_OR_ORGANIZATION/GITHUB_REPOSITORY/settings/pages`) - * Add the `main` branch and the `v*.*.*` tag rules to the "deployment branches and tags" list in the `gh-pages` environment (at URL `https://github.com/GITHUB_NAME_OR_ORGANIZATION/GITHUB_REPOSITORY/settings/environments`) - -**IMPORTANT:** The repository is configured to deploy on the [test PyPI repository](https://test.pypi.org/). -It's strongly recommended to create the project in the [test PyPI repository](https://test.pypi.org/) and test -the deployment pipeline. When you're happy with the result, create the project on the official [PyPI repository](https://pypi.org/) -and remove the marked lines in `workflows/release.yml`. - -## Package release - -This setup uses [poetry-dynamic-versioning](https://github.com/mtkennerly/poetry-dynamic-versioning). -This means it's not necessary to commit the version in the code but the CI pipeline -will infer it from the git tag. - -To release a new version, just create a new release and tag in the GitHub repository, to: - -* Build and deploy the python package to PyPI -* Build and deploy a new version of the documentation to GitHub pages - -**IMPORTANT:** The default configuration requires the release name and the tag to follow -the convention `vX.X.X` (semantic versioning preceded by lowercase `v`). It will publish -the correct version on Pypi, omitting the `v` (ie. `v1.0.0` will publish `1.0.0`). - -This format can be customized, refer to [poetry-dynamic-versioning docs](https://github.com/mtkennerly/poetry-dynamic-versioning) - -## Commands for development - -All the common commands used during development can be run using make targets: - -* `make dev-dependencies`: Install dev requirements -* `make update-dependencies`: Update dev requirements -* `make test`: Run test suite -* `make check`: Run tests, code style and lint checks -* `make fix`: Run code style and lint automatic fixes (where possible) -* `make docs`: Render the mkdocs website locally +--8<-- "./README.md" diff --git a/mkdocs-overrides/main.html b/mkdocs-overrides/main.html index 0af326a..90c78a2 100644 --- a/mkdocs-overrides/main.html +++ b/mkdocs-overrides/main.html @@ -1,3 +1,26 @@ +<!--=========================================================================--> +<!-- Copyright (c) 2024 Federico Busetti --> +<!-- <729029+febus982@users.noreply.github.com> --> +<!-- --> +<!-- Permission is hereby granted, free of charge, to any person obtaining a --> +<!-- copy of this software and associated documentation files (the "Software"), --> +<!-- to deal in the Software without restriction, including without limitation --> +<!-- the rights to use, copy, modify, merge, publish, distribute, sublicense, --> +<!-- and/or sell copies of the Software, and to permit persons to whom the --> +<!-- Software is furnished to do so, subject to the following conditions: --> +<!-- --> +<!-- The above copyright notice and this permission notice shall be included in --> +<!-- all copies or substantial portions of the Software. --> +<!-- --> +<!-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --> +<!-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, --> +<!-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL --> +<!-- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER --> +<!-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING --> +<!-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER --> +<!-- DEALINGS IN THE SOFTWARE. --> +<!--=========================================================================--> + {% extends "base.html" %} {% block outdated %} diff --git a/mkdocs.yml b/mkdocs.yml index 80ab349..3a70407 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -10,6 +10,10 @@ repo_url: 'https://github.com/febus982/bootstrap-python-package' plugins: - search - mike + - awesome-pages + - macros: + modules: + - mkdocs_macros_adr_summary - gen-files: scripts: - scripts/gen_pages.py # or any other name or path @@ -20,6 +24,12 @@ plugins: docstring_style: sphinx docstring_section_style: spacy +# Do not use the nav section in this file but reference to the .pages files +# in the docs/ directory and subdirectories (awesome-pages plugin) +# https://github.com/lukasgeiter/mkdocs-awesome-pages-plugin +#nav: +# - Home: index.md + theme: name: material custom_dir: mkdocs-overrides diff --git a/pyproject.toml b/pyproject.toml index 7628157..fecb142 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,61 +19,59 @@ classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Typing :: Typed" ] [tool.poetry-dynamic-versioning] -enable = true +enable = false [build-system] requires = ["poetry-core", "poetry-dynamic-versioning"] build-backend = "poetry_dynamic_versioning.backend" +############################ +### Package requirements ### +############################ + [tool.poetry.dependencies] -python = ">=3.8,<3.13" +python = ">=3.9,<3.14" [tool.poetry.group.dev] optional = true [tool.poetry.group.dev.dependencies] coverage = ">=6.5.0" -bandit = ">=1.7.6" -black = ">=22.10.0" mkdocs = ">=1.4.3" mkdocstrings = { version = ">=0.24.0", extras = ["python"] } +mkdocs-awesome-pages-plugin = "*" +mkdocs-macros-adr-summary = "*" mkdocs-gen-files = ">=0.5.0" mkdocs-material = ">=9.1.16" mike = ">=2.0.0" mypy = ">=0.990" pymdown-extensions = ">=10.0.1" -pytest = ">=7.2.0" +pytest = "^8.0.0" pytest-asyncio = ">=0.20.3" pytest-cov = ">=4.0.0" pytest-factoryboy = ">=2.5.0" pytest-xdist = ">=3.0.2" ruff = ">=0.0.263" +tox = ">=4.12.1" -[tool.pytest.ini_options] -asyncio_mode = "auto" -minversion = "6.0" -addopts = "-n auto --cov-report=term-missing" -testpaths = [ - "tests", -] - -[tool.mypy] -files = "bootstrap_python_package" +############################ +### Tools configuration ### +############################ [tool.coverage.run] branch = true source = ["bootstrap_python_package"] -concurrency = ["multiprocessing"] -parallel = true +# It's not necessary to configure concurrency here +# because pytest-cov takes care of that [tool.coverage.report] fail_under = 100 @@ -83,17 +81,52 @@ exclude_also = [ "\\.\\.\\.", ] +[tool.mypy] +files = "bootstrap_python_package" +python_version = "3.9" + +[tool.pytest.ini_options] +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "function" +minversion = "6.0" +addopts = "-n auto --cov-report=term-missing" +testpaths = [ + "tests", +] + [tool.ruff] -select = ["E", "F", "I"] -extend-exclude = ["docs"] +extend-exclude = ["docs", ".tox"] +target-version = "py39" -[tool.ruff.per-file-ignores] -"__init__.py" = ["F401"] +[tool.ruff.lint] +select = [ + "E", # pycodestyle + "W", # pycodestyle + "F", # pyflakes + "I", # isort + "N", # pep8-naming + "S", # flake8-bandit + "RUF", # ruff-specific-rules +] +# Ignoring rules problematic with formatter +# https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules +ignore = [ + "W191", + "E111", + "E114", + "E117", + "D206", + "D300", + "Q000", + "Q001", + "Q002", + "Q003", + "COM812", + "COM819", + "ISC001", + "ISC002", +] -[tool.black] -target-version = ["py38", "py39", "py310", "py311", "py312"] -extend-exclude = ''' -( - /docs -) -''' +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["F401"] # Ignore unused imports on init files +"tests/**/*.py" = ["S101"] # Allow assert usage on tests diff --git a/renovate.json b/renovate.json index 5db72dd..7cbfae7 100644 --- a/renovate.json +++ b/renovate.json @@ -2,5 +2,19 @@ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:recommended" + ], + "packageRules": [ + { + "groupName": "all non-major dependencies", + "groupSlug": "all-minor-patch", + "matchPackagePatterns": [ + "*" + ], + "excludePackageNames": ["python"], + "matchUpdateTypes": [ + "minor", + "patch" + ] + } ] } diff --git a/scripts/docs-version.sh b/scripts/docs-version.sh index a94be72..7212215 100755 --- a/scripts/docs-version.sh +++ b/scripts/docs-version.sh @@ -1,5 +1,29 @@ #!/usr/bin/env bash + +#=============================================================================== +# Copyright (c) 2024 Federico Busetti = +# <729029+febus982@users.noreply.github.com> = +# = +# Permission is hereby granted, free of charge, to any person obtaining a = +# copy of this software and associated documentation files (the "Software"), = +# to deal in the Software without restriction, including without limitation = +# the rights to use, copy, modify, merge, publish, distribute, sublicense, = +# and/or sell copies of the Software, and to permit persons to whom the = +# Software is furnished to do so, subject to the following conditions: = +# = +# The above copyright notice and this permission notice shall be included in = +# all copies or substantial portions of the Software. = +# = +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR = +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, = +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL = +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER = +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING = +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER = +# DEALINGS IN THE SOFTWARE. = +#=============================================================================== + VERSION=$(poetry version -s) SEMVER=( ${VERSION//./ } ) echo "${SEMVER[0]}.${SEMVER[1]}" diff --git a/scripts/gen_pages.py b/scripts/gen_pages.py index 5559e08..4ee3091 100644 --- a/scripts/gen_pages.py +++ b/scripts/gen_pages.py @@ -1,3 +1,35 @@ +# ============================================================================== +# Copyright (c) 2024 Federico Busetti = +# <729029+febus982@users.noreply.github.com> = +# = +# Permission is hereby granted, free of charge, to any person obtaining a = +# copy of this software and associated documentation files (the "Software"), = +# to deal in the Software without restriction, including without limitation = +# the rights to use, copy, modify, merge, publish, distribute, sublicense, = +# and/or sell copies of the Software, and to permit persons to whom the = +# Software is furnished to do so, subject to the following conditions: = +# = +# The above copyright notice and this permission notice shall be included in = +# all copies or substantial portions of the Software. = +# = +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR = +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, = +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL = +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER = +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING = +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER = +# DEALINGS IN THE SOFTWARE. = +# ============================================================================== + +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# # -----------------------------------------------------# # Library imports # # -----------------------------------------------------# @@ -35,7 +67,7 @@ full_doc_path = Path(nav_pages_path, doc_path) # Handle edge cases - parts = (src_dir,) + tuple(module_path.parts) + parts = (src_dir, *tuple(module_path.parts)) if parts[-1] == "__init__": parts = parts[:-1] doc_path = doc_path.with_name("index.md") diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..63c2f7d 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1,23 @@ +# ============================================================================== +# Copyright (c) 2024 Federico Busetti = +# <729029+febus982@users.noreply.github.com> = +# = +# Permission is hereby granted, free of charge, to any person obtaining a = +# copy of this software and associated documentation files (the "Software"), = +# to deal in the Software without restriction, including without limitation = +# the rights to use, copy, modify, merge, publish, distribute, sublicense, = +# and/or sell copies of the Software, and to permit persons to whom the = +# Software is furnished to do so, subject to the following conditions: = +# = +# The above copyright notice and this permission notice shall be included in = +# all copies or substantial portions of the Software. = +# = +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR = +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, = +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL = +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER = +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING = +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER = +# DEALINGS IN THE SOFTWARE. = +# ============================================================================== + diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index 15d16b5..cadb891 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -1,3 +1,26 @@ +# ============================================================================== +# Copyright (c) 2024 Federico Busetti = +# <729029+febus982@users.noreply.github.com> = +# = +# Permission is hereby granted, free of charge, to any person obtaining a = +# copy of this software and associated documentation files (the "Software"), = +# to deal in the Software without restriction, including without limitation = +# the rights to use, copy, modify, merge, publish, distribute, sublicense, = +# and/or sell copies of the Software, and to permit persons to whom the = +# Software is furnished to do so, subject to the following conditions: = +# = +# The above copyright notice and this permission notice shall be included in = +# all copies or substantial portions of the Software. = +# = +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR = +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, = +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL = +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER = +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING = +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER = +# DEALINGS IN THE SOFTWARE. = +# ============================================================================== + from bootstrap_python_package import some_function diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..ff1c841 --- /dev/null +++ b/tox.ini @@ -0,0 +1,40 @@ +[tox] +min_version = 4.0 +env_list = + py313 + py312 + py311 + py310 + py39 + typing + lint + format + +[testenv] +; The file /tmp/requirements.txt is created automatically if you run tox +; using `make check` command, otherwise manually run +; `poetry export -f requirements.txt --output /tmp/requirements.txt --with dev` +; Poetry is really bad in identifying running virtualenvs, so we can't use +; directly poetry install. This is the best hacky way to install poetry +; requirements inside tox. +deps = + -r/tmp/requirements.txt +commands = + pytest + +[testenv:py313] +; Run with coverage in one python version to check coverage percentage +commands = + pytest --cov + +[testenv:typing] +commands = + mypy + +[testenv:format] +commands = + ruff format --check . + +[testenv:lint] +commands = + ruff check .