diff --git a/.copier-answers.yml b/.copier-answers.yml new file mode 100644 index 0000000..fc5ee7c --- /dev/null +++ b/.copier-answers.yml @@ -0,0 +1,21 @@ +# Changes here will be overwritten by Copier +# Do not modify this file directly +# To update the project using the original template, run make copier-update +_commit: v1.0.0 +_src_path: gh:ONSdigital/ons-python-template +default_branch: main +dependabot_open_pull_requests_limit: 10 +dismiss_stale_reviews: false +enable_dependabot_version_updates: true +module_name: dis_wagtail +package_manager: poetry +repo_settings: custom +repository_description: The Django Wagtail CMS for managing and publishing content + for the Office for National Statistics (ONS) +repository_name: dis-wagtail +repository_owner: ONSdigital +repository_visibility: public +require_conversation_resolution: true +require_last_push_approval: false +required_approving_review_count: 2 +set_up_git_repo: true diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..876f20b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,42 @@ +# http://editorconfig.org + +# A special property that should be specified at the top of the file outside of +# any sections. Set to true to stop .editor config file search on current file +root = true + +[*] +# Indentation style +# Possible values - tab, space +indent_style = space + +# Character length allowed +max_line_length = 120 + +# Indentation size in single-spaced characters +# Possible values - an integer, tab +indent_size = 4 + +# Line ending file format +# Possible values - lf, crlf, cr +end_of_line = lf + +# File character encoding +# Possible values - latin1, utf-8, utf-16be, utf-16le +charset = utf-8 + +# Denotes whether to trim whitespace at the end of lines +# Possible values - true, false +trim_trailing_whitespace = true + +# Denotes whether file should end with a newline +# Possible values - true, false +insert_final_newline = true + +[*.{js,html,json,yaml,yml}] +indent_size = 2 + +[*.{json,yaml,yml}] +max_line_length = 160 + +[Makefile] +indent_style = tab diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..4ebb0a6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,35 @@ +### Summary + +Provide a brief summary of the issue you're encountering. + +### Steps to Reproduce + +Describe the steps you took when you encountered the bug: + +1. ... +2. ... +3. ... + +### Expected Behavior + +What did you expect to happen when you followed the steps above? + +### Actual Behavior + +Describe what actually happened. Include details like error messages or unexpected outcomes. + +### Technical Details + +Any relevant technical information. Include as much information as possible, including: + +- **Operating System**: +- **Browser/Application Version**: +- **Additional relevant software versions**: + +### Proposed Solutions (Optional) + +If you have any suggestions on how to fix this issue, please share them here. + +### Screenshots + +Attach any screenshots that help illustrate the issue. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..e216ebd --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,11 @@ +### What is the context of this PR? + +Describe what you have changed and why, link to other PRs or Issues as appropriate. + +### How to review + +Describe the steps required to test the changes (include screenshots if appropriate). + +### Follow-up Actions + +List any follow-up actions (if applicable), like needed documentation updates or additional testing. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..a0f1367 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,48 @@ +--- +# Config for Dependabot updates. See Documentation here: +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + # Update GitHub actions in workflows + - package-ecosystem: github-actions + directory: / + # Every week + schedule: + interval: weekly + + groups: + # Group updates into fewer pull requests + gh-security-updates: + applies-to: security-updates + patterns: + - "*" + + gh-version-updates: + applies-to: version-updates + patterns: + - "*" + + # Enable Security updates for Python package manager + - package-ecosystem: pip + + # When set to 0, version updates are disabled. + open-pull-requests-limit: 10 + + # Look for a pyproject.toml/requirements.txt/Pipfile.lock in the root directory + directory: / + # Every weekday + schedule: + interval: daily + + groups: + # Group updates into fewer pull requests + py-security-updates: + applies-to: security-updates + patterns: + - "*" + + py-version-updates: + applies-to: version-updates + patterns: + - "*" diff --git a/.github/linters/.checkov.yml b/.github/linters/.checkov.yml new file mode 100644 index 0000000..7ac591b --- /dev/null +++ b/.github/linters/.checkov.yml @@ -0,0 +1,11 @@ +--- +# https://github.com/bridgecrewio/checkov +compact: true +download-external-modules: false +evaluate-variables: true +output: cli +quiet: true +soft-fail: false +skip-check: + - CKV2_GHA_1 # Not needed +summary-position: bottom diff --git a/.github/linters/.markdown-link-check.json b/.github/linters/.markdown-link-check.json new file mode 100644 index 0000000..56bc23c --- /dev/null +++ b/.github/linters/.markdown-link-check.json @@ -0,0 +1,6 @@ +{ + "ignorePatterns": [], + "retryOn429": true, + "retryCount": 3, + "fallbackRetryDelay": "30s" +} diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml new file mode 100644 index 0000000..35e5c54 --- /dev/null +++ b/.github/linters/.markdown-lint.yml @@ -0,0 +1,366 @@ +--- +# https://github.com/DavidAnson/markdownlint +# Example markdownlint configuration with all properties set to their default value + +# Default state for all rules +default: true + +# Path to configuration file to extend +extends: + +# MD001/heading-increment +# Heading levels should only increment by one level at a time +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md001.md +MD001: true + +# MD003/heading-style +# Heading style +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md003.md +MD003: + # Heading style + style: consistent + +# MD004/ul-style +# Unordered list style +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md004.md +MD004: + # List style + style: consistent + +# MD005/list-indent +# Inconsistent indentation for list items at the same level +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md005.md +MD005: true + +# MD007/ul-indent +# Unordered list indentation +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md007.md +MD007: + # Spaces for indent + indent: 4 + # Whether to indent the first level of the list + start_indented: false + # Spaces for first level indent (when start_indented is set) + start_indent: 4 + +# MD009/no-trailing-spaces +# Trailing spaces +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md009.md +MD009: + # Spaces for line break + br_spaces: 2 + # Allow spaces for empty lines in list items + list_item_empty_lines: false + # Include unnecessary breaks + strict: false + +# MD010/no-hard-tabs +# Hard tabs +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md010.md +MD010: + # Include code blocks + code_blocks: true + # Fenced code languages to ignore + ignore_code_languages: [] + # Number of spaces for each hard tab + spaces_per_tab: 1 + +# MD011/no-reversed-links +# Reversed link syntax +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md011.md +MD011: true + +# MD012/no-multiple-blanks +# Multiple consecutive blank lines +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md012.md +MD012: + # Consecutive blank lines + maximum: 1 + +# MD013/line-length +# Line length +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md013.md +MD013: + # Number of characters + line_length: 120 + # Number of characters for headings + heading_line_length: 80 + # Number of characters for code blocks + code_block_line_length: 120 + # Include code blocks + code_blocks: true + # Include tables + tables: true + # Include headings + headings: true + # Strict length checking + strict: false + # Stern length checking + stern: false + +# MD014/commands-show-output +# Dollar signs used before commands without showing output +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md014.md +MD014: true + +# MD018/no-missing-space-atx +# No space after hash on atx style heading +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md018.md +MD018: true + +# MD019/no-multiple-space-atx +# Multiple spaces after hash on atx style heading +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md019.md +MD019: true + +# MD020/no-missing-space-closed-atx +# No space inside hashes on closed atx style heading +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md020.md +MD020: true + +# MD021/no-multiple-space-closed-atx +# Multiple spaces inside hashes on closed atx style heading +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md021.md +MD021: true + +# MD022/blanks-around-headings +# Headings should be surrounded by blank lines +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md022.md +MD022: + # Blank lines above heading + lines_above: 1 + # Blank lines below heading + lines_below: 1 + +# MD023/heading-start-left +# Headings must start at the beginning of the line +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md023.md +MD023: true + +# MD024/no-duplicate-heading +# Multiple headings with the same content +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md024.md +MD024: + # Only check sibling headings + siblings_only: false + +# MD025/single-title/single-h1 +# Multiple top-level headings in the same document +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md025.md +MD025: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: ^\s*title\s*[:=] + +# MD026/no-trailing-punctuation +# Trailing punctuation in heading +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md026.md +MD026: + # Punctuation characters + punctuation: .,;:!。,;:! + +# MD027/no-multiple-space-blockquote +# Multiple spaces after blockquote symbol +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md027.md +MD027: true + +# MD028/no-blanks-blockquote +# Blank line inside blockquote +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md028.md +MD028: true + +# MD029/ol-prefix +# Ordered list item prefix +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md029.md +MD029: + # List style + style: one_or_ordered + +# MD030/list-marker-space +# Spaces after list markers +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md030.md +MD030: + # Spaces for single-line unordered list items + ul_single: 1 + # Spaces for single-line ordered list items + ol_single: 1 + # Spaces for multi-line unordered list items + ul_multi: 1 + # Spaces for multi-line ordered list items + ol_multi: 1 + +# MD031/blanks-around-fences +# Fenced code blocks should be surrounded by blank lines +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md031.md +MD031: + # Include list items + list_items: true + +# MD032/blanks-around-lists +# Lists should be surrounded by blank lines +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md032.md +MD032: true + +# MD033/no-inline-html +# Inline HTML +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md033.md +MD033: + # Allowed elements + allowed_elements: [] + +# MD034/no-bare-urls +# Bare URL used +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md034.md +MD034: true + +# MD035/hr-style +# Horizontal rule style +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md035.md +MD035: + # Horizontal rule style + style: consistent + +# MD036/no-emphasis-as-heading +# Emphasis used instead of a heading +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md036.md +MD036: + # Punctuation characters + punctuation: .,;:!?。,;:!? + +# MD037/no-space-in-emphasis +# Spaces inside emphasis markers +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md037.md +MD037: true + +# MD038/no-space-in-code +# Spaces inside code span elements +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md038.md +MD038: true + +# MD039/no-space-in-links +# Spaces inside link text +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md039.md +MD039: true + +# MD040/fenced-code-language +# Fenced code blocks should have a language specified +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md040.md +MD040: + # List of languages + allowed_languages: [] + # Require language only + language_only: false + +# MD041/first-line-heading/first-line-h1 +# First line in a file should be a top-level heading +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md041.md +MD041: false + +# MD042/no-empty-links +# No empty links +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md042.md +MD042: true + +# MD043/required-headings +# Required heading structure +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md043.md +MD043: true + +# MD044/proper-names +# Proper names should have the correct capitalization +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md044.md +MD044: + # List of proper names + names: [] + # Include code blocks + code_blocks: true + # Include HTML elements + html_elements: true + +# MD045/no-alt-text +# Images should have alternate text (alt text) +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md045.md +MD045: true + +# MD046/code-block-style +# Code block style +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md046.md +MD046: + # Block style + style: consistent + +# MD047/single-trailing-newline +# Files should end with a single newline character +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md047.md +MD047: true + +# MD048/code-fence-style +# Code fence style +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md048.md +MD048: + # Code fence style + style: consistent + +# MD049/emphasis-style +# Emphasis style +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md049.md +MD049: + # Emphasis style + style: consistent + +# MD050/strong-style +# Strong style +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md050.md +MD050: + # Strong style + style: consistent + +# MD051/link-fragments +# Link fragments should be valid +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md051.md +MD051: true + +# MD052/reference-links-images +# Reference links and images should use a label that is defined +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md052.md +MD052: + # Include shortcut syntax + shortcut_syntax: false + +# MD053/link-image-reference-definitions +# Link and image reference definitions should be needed +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md053.md +MD053: + # Ignored definitions + ignored_definitions: + - // + +# MD054/link-image-style +# Link and image style +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md054.md +MD054: + # Allow autolinks + autolink: true + # Allow inline links and images + inline: true + # Allow full reference links and images + full: true + # Allow collapsed reference links and images + collapsed: true + # Allow shortcut reference links and images + shortcut: true + # Allow URLs as inline links + url_inline: true + +# MD055/table-pipe-style +# Table pipe style +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md055.md +MD055: + # Table pipe style + style: consistent + +# MD056/table-column-count +# Table column count +# https://github.com/DavidAnson/markdownlint/blob/v0.33.0/doc/md056.md +MD056: true diff --git a/.github/linters/.yaml-lint.yml b/.github/linters/.yaml-lint.yml new file mode 100644 index 0000000..e4e103b --- /dev/null +++ b/.github/linters/.yaml-lint.yml @@ -0,0 +1,63 @@ +--- +# https://github.com/adrienverge/yamllint +########################################### +# These are the rules used for # +# linting all the yaml files in the stack # +# NOTE: # +# You can disable line with: # +# # yamllint disable-line # +########################################### +rules: + braces: + level: error + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: 1 + max-spaces-inside-empty: 5 + brackets: + level: error + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: 0 + max-spaces-inside-empty: 5 + colons: + level: error + max-spaces-before: 0 + max-spaces-after: 1 + commas: + level: error + max-spaces-before: 0 + min-spaces-after: 1 + max-spaces-after: 1 + comments: + require-starting-space: true + ignore-shebangs: true + min-spaces-from-content: 1 # Used to follow prettier standard: https://github.com/prettier/prettier/pull/10926 + comments-indentation: true + document-end: disable + document-start: + level: error + present: true + empty-lines: + level: error + max: 2 + max-start: 0 + max-end: 0 + hyphens: + level: error + max-spaces-after: 1 + indentation: + level: error + spaces: consistent + indent-sequences: true + check-multi-line-strings: false + key-duplicates: enable + line-length: + level: error + max: 160 + allow-non-breakable-words: true + allow-non-breakable-inline-mappings: true + new-line-at-end-of-file: disable + new-lines: + type: unix + trailing-spaces: disable diff --git a/.github/linters/kics.config b/.github/linters/kics.config new file mode 100644 index 0000000..e848737 --- /dev/null +++ b/.github/linters/kics.config @@ -0,0 +1,5 @@ +# vim: set filetype=yaml: + +# https://docs.kics.io/latest/ +exclude-queries: + - "555ab8f9-2001-455e-a077-f2d0f41e2fb9" # Allow not pinning to commit SHA in GitHub CI diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..6f3e433 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,20 @@ +changelog: + categories: + - title: ⚠️ Breaking Changes + labels: + - Breaking Change + - title: New Features + labels: + - New Feature + - title: Enhancements + labels: + - Enhancement + - title: Bug Fixes + labels: + - Bug Fix + - title: Documentation + labels: + - Documentation + - title: Other Changes + labels: + - "*" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..261f9b4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +--- +name: CI + +on: # yamllint disable-line rule:truthy + push: + branches: [main] + pull_request: + branches: [main] + +concurrency: + group: "${{ github.head_ref || github.ref }}-${{ github.workflow }}" + cancel-in-progress: true + +jobs: + lint-test: + name: Lint and Test + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Install Poetry + run: pipx install poetry==1.7.1 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version-file: .python-version + cache: poetry + + - name: Install dependencies + run: make install-dev + + - name: Lint Python + run: make lint + + - name: Test + run: make test diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..1dd1b6b --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,83 @@ +--- +# https://docs.github.com/en/code-security/code-scanning/introduction-to-code-scanning/about-code-scanning-with-codeql +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +name: CodeQL + +on: # yamllint disable-line rule:truthy + push: + branches: [main] + pull_request: + # The branches below must be a subset of the branches above + branches: [main] + + schedule: + # Run on a daily interval at 12pm UTC + - cron: "0 12 * * *" + +concurrency: + group: "${{ github.head_ref || github.ref }}-${{ github.workflow }}" + cancel-in-progress: true + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-22.04 + timeout-minutes: 60 + permissions: + # required for all workflows + security-events: write + + strategy: + fail-fast: false + matrix: + language: [python] + # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ] + # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: + # https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + setup-python-dependencies: false + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: /language:${{matrix.language}} diff --git a/.github/workflows/mega-linter.yml b/.github/workflows/mega-linter.yml new file mode 100644 index 0000000..f755ca3 --- /dev/null +++ b/.github/workflows/mega-linter.yml @@ -0,0 +1,198 @@ +# MegaLinter GitHub Action configuration file +# More info at https://megalinter.io +--- +name: MegaLinter + +# Trigger mega-linter at every push. Action will also be visible from +# Pull Requests to main +on: # yamllint disable-line rule:truthy + push: + branches: [main] + pull_request: + branches: [main] + +# Comment env block if you do not want to apply fixes +env: + # Apply linter fixes configuration + # + # When active, APPLY_FIXES must also be defined as environment variable + # (in github/workflows/mega-linter.yml or other CI tool) + # This is dynamically set based on the presence of the PAT secret. + # If the PAT secret is not present, the APPLY_FIXES environment variable is set to none. + # Without a PAT token, commits/PRs will not trigger workflow runs. + # This is a GitHub Actions limitation to prevent infinite loops. + APPLY_FIXES: all + + # Decide which event triggers application of fixes in a commit or a PR + # (pull_request, push, all) + APPLY_FIXES_EVENT: pull_request + + # If APPLY_FIXES is used, defines if the fixes are directly committed (commit) + # or posted in a PR (pull_request) + APPLY_FIXES_MODE: commit + + # Show individual linter status in GitHub Actions status summary + GITHUB_STATUS_REPORTER: true + + # Enable to show lint results in GitHub PR comments. Disabled as it can be noisy. + GITHUB_COMMENT_REPORTER: false + + # Set to simple to avoid external images in generated markdown + REPORTERS_MARKDOWN_TYPE: simple + +concurrency: + group: "${{ github.head_ref || github.ref }}-${{ github.workflow }}" + cancel-in-progress: true + +jobs: + lint: + name: MegaLinter + runs-on: ubuntu-latest + + # Give the default GITHUB_TOKEN write permission to commit and push, comment + # issues, and post new Pull Requests; remove the ones you do not need + permissions: + contents: write + issues: write + pull-requests: write + statuses: write + + steps: + # Git Checkout + - name: Checkout Code + uses: actions/checkout@v4 + with: + token: ${{ secrets.PAT || secrets.GITHUB_TOKEN }} + + # If you use VALIDATE_ALL_CODEBASE = true, you can remove this line to + # improve performance + fetch-depth: 0 + + - name: Check PAT and Set APPLY_FIXES + run: | + if [ -z "${{ secrets.PAT }}" ]; then + echo "APPLY_FIXES=none" >> "$GITHUB_ENV" + fi + + # MegaLinter + - name: MegaLinter + + # You can override MegaLinter flavor used to have faster performances + # More info at https://megalinter.io/latest/flavors/ + uses: oxsecurity/megalinter@v7 + + id: ml + + # All available variables are described in documentation + # https://megalinter.io/latest/config-file/ + env: + # Validates all source when push on main, else just the git diff with + # main. Override with true if you always want to lint all sources + # + # To validate the entire codebase, set to: + # VALIDATE_ALL_CODEBASE: true + # + # To validate only diff with main, set to: + # VALIDATE_ALL_CODEBASE: >- + # ${{ + # github.event_name == 'push' && + # github.ref == 'refs/heads/main' + # }} + VALIDATE_ALL_CODEBASE: true + + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # ADD YOUR CUSTOM ENV VARIABLES HERE TO OVERRIDE VALUES OF + # .mega-linter.yml AT THE ROOT OF YOUR REPOSITORY + + # Upload MegaLinter artifacts + - name: Archive production artifacts + uses: actions/upload-artifact@v4 + if: success() || failure() + with: + name: MegaLinter reports + path: | + megalinter-reports + mega-linter.log + + # Create pull request if applicable + # (for now works only on PR from same repository, not from forks) + - name: Create Pull Request with applied fixes + uses: peter-evans/create-pull-request@v6 + id: cpr + if: >- + steps.ml.outputs.has_updated_sources == 1 && + ( + env.APPLY_FIXES_EVENT == 'all' || + env.APPLY_FIXES_EVENT == github.event_name + ) && + env.APPLY_FIXES_MODE == 'pull_request' && + ( + github.event_name == 'push' || + github.event.pull_request.head.repo.full_name == github.repository + ) && + !contains(github.event.head_commit.message, 'skip fix') + with: + token: ${{ secrets.PAT || secrets.GITHUB_TOKEN }} + commit-message: "[MegaLinter] Apply linters automatic fixes" + title: "[MegaLinter] Apply linters automatic fixes" + labels: bot + + - name: Create PR output + if: >- + steps.ml.outputs.has_updated_sources == 1 && + ( + env.APPLY_FIXES_EVENT == 'all' || + env.APPLY_FIXES_EVENT == github.event_name + ) && + env.APPLY_FIXES_MODE == 'pull_request' && + ( + github.event_name == 'push' || + github.event.pull_request.head.repo.full_name == github.repository + ) && + !contains(github.event.head_commit.message, 'skip fix') + run: | + echo "PR Number - ${{ steps.cpr.outputs.pull-request-number }}" + echo "PR URL - ${{ steps.cpr.outputs.pull-request-url }}" + + # Push new commit if applicable + # (for now works only on PR from same repository, not from forks) + - name: Prepare commit + if: >- + steps.ml.outputs.has_updated_sources == 1 && + ( + env.APPLY_FIXES_EVENT == 'all' || + env.APPLY_FIXES_EVENT == github.event_name + ) && + env.APPLY_FIXES_MODE == 'commit' && + github.ref != 'refs/heads/main' && + ( + github.event_name == 'push' || + github.event.pull_request.head.repo.full_name == github.repository + ) && + !contains(github.event.head_commit.message, 'skip fix') + run: sudo chown -Rc $UID .git/ + + - name: Commit and push applied linter fixes + uses: stefanzweifel/git-auto-commit-action@v5 + if: >- + steps.ml.outputs.has_updated_sources == 1 && + ( + env.APPLY_FIXES_EVENT == 'all' || + env.APPLY_FIXES_EVENT == github.event_name + ) && + env.APPLY_FIXES_MODE == 'commit' && + github.ref != 'refs/heads/main' && + ( + github.event_name == 'push' || + github.event.pull_request.head.repo.full_name == github.repository + ) && + !contains(github.event.head_commit.message, 'skip fix') + with: + branch: >- + ${{ + github.event.pull_request.head.ref || + github.head_ref || + github.ref + }} + commit_message: "[MegaLinter] Apply linters fixes" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7690b47 --- /dev/null +++ b/.gitignore @@ -0,0 +1,171 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +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 + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +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.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# 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/ + +# Ruff +.ruff_cache/ + +megalinter-reports/ + +# Copier helpers +copier_scripts/ + +# DS_Store files +.DS_Store diff --git a/.mega-linter.yml b/.mega-linter.yml new file mode 100644 index 0000000..cdfcce4 --- /dev/null +++ b/.mega-linter.yml @@ -0,0 +1,40 @@ +--- +# Configuration file for MegaLinter +# +# See all available variables at https://megalinter.io/latest/config-file/ and in +# linters documentation + +# all, none, or list of linter keys +APPLY_FIXES: all + +FORMATTERS_DISABLE_ERRORS: false + +# If you use ENABLE variable, all other languages/formats/tooling-formats will +# be disabled by default +# ENABLE: + +# If you use ENABLE_LINTERS variable, all other linters will be disabled by +# default +# ENABLE_LINTERS: + +DISABLE: + - COPYPASTE + - PYTHON + - EDITORCONFIG + - SPELL + +DISABLE_LINTERS: + - REPOSITORY_DEVSKIM + - REPOSITORY_TRUFFLEHOG + - REPOSITORY_GIT_DIFF + +SHOW_ELAPSED_TIME: true + +FILEIO_REPORTER: false +# Uncomment if you want MegaLinter to detect errors but not block CI to pass +# DISABLE_ERRORS: true + +# Use yml file to allow the use of comments. Megalinter's default is .markdown-lint.json +MARKDOWN_MARKDOWNLINT_CONFIG_FILE: .markdown-lint.yml + +YAML_V8R_FILTER_REGEX_EXCLUDE: "dependabot.yml" # Dependabot yaml in SchemaStore is currently not up to date. Dependabot is validated on push by GitHub by default. diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..86b080e --- /dev/null +++ b/.pylintrc @@ -0,0 +1,635 @@ +[MAIN] + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Clear in-memory caches upon conclusion of linting. Useful if running pylint +# in a server-like mode. +clear-cache-post-run=no + +# Load and enable all available extensions. Use --list-extensions to see a list +# all available extensions. +#enable-all-extensions= + +# In error mode, messages with a category besides ERROR or FATAL are +# suppressed, and no reports are done by default. Error mode is compatible with +# disabling specific errors. +#errors-only= + +# Always return a 0 (non-error) status code, even if lint errors are found. +# This is primarily useful in continuous integration scripts. +#exit-zero= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +extension-pkg-allow-list= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. (This is an alternative name to extension-pkg-allow-list +# for backward compatibility.) +extension-pkg-whitelist= + +# Return non-zero exit code if any of these messages/categories are detected, +# even if score is above --fail-under value. Syntax same as enable. Messages +# specified are enabled, while categories only check already-enabled messages. +fail-on= + +# Specify a score threshold under which the program will exit with error. +fail-under=10 + +# Interpret the stdin as a python script, whose filename needs to be passed as +# the module_or_package argument. +#from-stdin= + +# Files or directories to be skipped. They should be base names, not paths. +ignore=CVS + +# Add files or directories matching the regular expressions patterns to the +# ignore-list. The regex matches against paths and can be in Posix or Windows +# format. Because '\\' represents the directory delimiter on Windows systems, +# it can't be used as an escape character. +ignore-paths= + +# Files or directories matching the regular expression patterns are skipped. +# The regex matches against base names, not paths. The default value ignores +# Emacs file locks +ignore-patterns=^\.# + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis). It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use, and will cap the count on Windows to +# avoid hangs. +jobs=0 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Minimum Python version to use for version dependent checks. Will default to +# the version used to run pylint. +py-version=3.10 + +# Discover python modules and packages in the file system subtree. +recursive=no + +# Add paths to the list of the source roots. Supports globbing patterns. The +# source root is an absolute path or a path relative to the current working +# directory used to determine a package namespace for modules located under the +# source root. +source-roots= + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# In verbose mode, extra non-checker-related info will be displayed. +#verbose= + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. If left empty, argument names will be checked with the set +# naming style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. If left empty, attribute names will be checked with the set naming +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +bad-names-rgxs= + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. If left empty, class attribute names will be checked +# with the set naming style. +#class-attribute-rgx= + +# Naming style matching correct class constant names. +class-const-naming-style=UPPER_CASE + +# Regular expression matching correct class constant names. Overrides class- +# const-naming-style. If left empty, class constant names will be checked with +# the set naming style. +#class-const-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. If left empty, class names will be checked with the set naming style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. If left empty, constant names will be checked with the set naming +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. If left empty, function names will be checked with the set +# naming style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + ex, + Run, + _ + +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +good-names-rgxs= + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. If left empty, inline iteration names will be checked +# with the set naming style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. If left empty, method names will be checked with the set naming style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. If left empty, module names will be checked with the set naming style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Regular expression matching correct type alias names. If left empty, type +# alias names will be checked with the set naming style. +#typealias-rgx= + +# Regular expression matching correct type variable names. If left empty, type +# variable names will be checked with the set naming style. +#typevar-rgx= + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. If left empty, variable names will be checked with the set +# naming style. +#variable-rgx= + + +[CLASSES] + +# Warn about protected attribute access inside special methods +check-protected-access-in-special-methods=no + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp, + asyncSetUp, + __post_init__ + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make,os._exit + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[DESIGN] + +# List of regular expressions of class ancestor names to ignore when counting +# public methods (see R0903) +exclude-too-few-public-methods= + +# List of qualified class names to ignore when counting class parents (see +# R0901) +ignored-parents= + +# Maximum number of arguments for function / method. +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement (see R0916). +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when caught. +overgeneral-exceptions=builtins.BaseException,builtins.Exception + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[IMPORTS] + +# List of modules that can be imported at any level, not just the top level +# one. +allow-any-import-level= + +# Allow explicit reexports by alias from a package __init__. +allow-reexport-from-package=no + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules= + +# Output a graph (.gv or any supported image format) of external dependencies +# to the given file (report RP0402 must not be disabled). +ext-import-graph= + +# Output a graph (.gv or any supported image format) of all (i.e. internal and +# external) dependencies to the given file (report RP0402 must not be +# disabled). +import-graph= + +# Output a graph (.gv or any supported image format) of internal dependencies +# to the given file (report RP0402 must not be disabled). +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= + + +[LOGGING] + +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, +# UNDEFINED. +confidence=HIGH, + CONTROL_FLOW, + INFERENCE, + INFERENCE_FAILURE, + UNDEFINED + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then re-enable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable=raw-checker-failed, + bad-inline-option, + locally-disabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + use-symbolic-message-instead, + use-implicit-booleaness-not-comparison-to-string, + use-implicit-booleaness-not-comparison-to-zero, + duplicate-code + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable=c-extension-no-member,useless-suppression + + +[METHOD_ARGS] + +# List of qualified names (i.e., library.method) which require a timeout +# parameter e.g. 'requests.api.get,requests.api.post' +timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + +# Regular expression of note tags to take in consideration. +notes-rgx= + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit,argparse.parse_error + + +[REPORTS] + +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'fatal', 'error', 'warning', 'refactor', +# 'convention', and 'info' which contain the number of messages in each +# category, as well as 'statement' which is the total number of statements +# analyzed. This score is used by the global evaluation report (RP0004). +evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +msg-template= + +# Set the output format. Available formats are: text, parseable, colorized, +# json2 (improved json format), json (old json format) and msvs (visual +# studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +#output-format=colorized + +# Tells whether to display a full report or only the messages. +reports=yes + +# Activate the evaluation score. +score=yes + + +[SIMILARITIES] + +# Comments are removed from the similarity computation +ignore-comments=yes + +# Docstrings are removed from the similarity computation +ignore-docstrings=yes + +# Imports are removed from the similarity computation +ignore-imports=yes + +# Signatures are removed from the similarity computation +ignore-signatures=yes + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. No available dictionaries : You need to install +# both the python package and the system dependency for enchant to work. +spelling-dict= + +# List of comma separated words that should be considered directives if they +# appear at the beginning of a comment and should not be checked. +spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains the private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no + + +[STRING] + +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=no + +# This flag controls whether the implicit-str-concat should generate a warning +# on implicit string concatenation in sequences defined over several lines. +check-str-concat-over-line-jumps=no + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of symbolic message names to ignore for Mixin members. +ignored-checks-for-mixins=no-member, + not-async-context-manager, + not-context-manager, + attribute-defined-outside-init + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + +# Regex pattern to define which classes are considered mixins. +mixin-class-rgx=.*[Mm]ixin + +# List of decorators that change the signature of a decorated function. +signature-mutators= + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of names allowed to shadow builtins +allowed-redefined-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..09dcc78 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.10.11 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..0b1d9fa --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,153 @@ +# Code of conduct for `ONSdigital` repositories + +Contributors to repositories hosted in `ONSdigital` are expected to follow the [Contributor Covenant Code of +Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html), and those working within Government are +also expected to follow +the [Civil Service Code](https://www.gov.uk/government/publications/civil-service-code/the-civil-service-code)) + +## Civil Service Code + +Read the [Civil Service Code](https://www.gov.uk/government/publications/civil-service-code/the-civil-service-code). + +## Contributor Covenant Code of Conduct + +> Note: +> +> - where the code of conduct says "project" we mean ONS, `ONSdigital` and all repositories hosted within it. +> - where the code of conduct says "maintainer" we mean `ONSdigital` organisation owners +> - where the code of conduct says "leadership" we mean both `ONSdigital` organisation owners, line managers, and other + leadership within ONS + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologising to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +- The use of sexualised language or imagery, and sexual attention or advances of + any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, + without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org + +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html + +[Mozilla CoC]: https://github.com/mozilla/diversity + +[FAQ]: https://www.contributor-covenant.org/faq + +[translations]: https://www.contributor-covenant.org/translations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..832641f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,43 @@ +# Contributing Guidelines + +Pull requests, bug reports, and all other forms of contribution are welcomed and highly encouraged! +This guide serves to set clear expectations for everyone involved with the project so that we can improve it together +while also creating a welcoming space for everyone to participate. Following these guidelines will help ensure a +positive experience for contributors and maintainers. + +## Code of Conduct + +This project and everyone participating in it are governed by our [Code of Conduct](CODE_OF_CONDUCT.md). By +participating, you are expected to uphold this code. + +## How Can I Contribute? + +### Reporting Security Issues + +Review our [Security Policy](SECURITY.md) for information on how to report security vulnerabilities. + +### Reporting Bugs + +If you encounter a bug, please check the existing issues to see if it has already been reported. If not, please open a +new issue, providing as much detail as possible, including steps to reproduce. + +### Suggesting Enhancements + +We welcome suggestions for new features or enhancements. Please open an issue to discuss your ideas before starting work +on them. + +### Pull Requests + +We actively welcome your pull requests. To make the process as smooth as possible, please follow these guidelines: + +1. Fork the repository and create your branch from `main`. +2. If you've added code that should be tested, add tests. +3. Ensure that your code passes the existing tests. +4. Make sure your code lints. +5. Update the documentation to reflect any changes if necessary. +6. Open a pull request, providing a clear description of your changes and referencing any related issues. + +## License + +By contributing to this project, you agree that your contributions will be licensed under +the [project's license](LICENSE). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f0de560 --- /dev/null +++ b/LICENSE @@ -0,0 +1,16 @@ +The MIT License (MIT) +Copyright (c) 2024, Crown Copyright (Office for National Statistics) + +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/Makefile b/Makefile new file mode 100644 index 0000000..8fb019c --- /dev/null +++ b/Makefile @@ -0,0 +1,50 @@ +.DEFAULT_GOAL := all + +.PHONY: all +all: ## Show the available make targets. + @echo "Usage: make " + @echo "" + @echo "Targets:" + @fgrep "##" Makefile | fgrep -v fgrep + +.PHONY: clean +clean: ## Clean the temporary files. + rm -rf .pytest_cache + rm -rf .mypy_cache + rm -rf .coverage + rm -rf .ruff_cache + rm -rf megalinter-reports + +.PHONY: format +format: ## Format the code. + poetry run black . + poetry run ruff check . --fix + +.PHONY: lint +lint: ## Run all linters (black/ruff/pylint/mypy). + poetry run black --check . + poetry run ruff check . + make mypy + +.PHONY: test +test: ## Run the tests and check coverage. + poetry run pytest -n auto --cov=dis_wagtail --cov-report term-missing --cov-fail-under=100 + +.PHONY: mypy +mypy: ## Run mypy. + poetry run mypy dis_wagtail + +.PHONY: install +install: ## Install the dependencies excluding dev. + poetry install --only main --no-root + +.PHONY: install-dev +install-dev: ## Install the dependencies including dev. + poetry install --no-root + +.PHONY: megalint +megalint: ## Run the mega-linter. + docker run --platform linux/amd64 --rm \ + -v /var/run/docker.sock:/var/run/docker.sock:rw \ + -v $(shell pwd):/tmp/lint:rw \ + oxsecurity/megalinter:v7 diff --git a/README.md b/README.md new file mode 100644 index 0000000..4d6245f --- /dev/null +++ b/README.md @@ -0,0 +1,146 @@ +# dis-wagtail + +[![Build Status](https://github.com/ONSdigital/dis-wagtail/actions/workflows/ci.yml/badge.svg)](https://github.com/ONSdigital/dis-wagtail/actions/workflows/ci.yml) +[![Build Status](https://github.com/ONSdigital/dis-wagtail/actions/workflows/mega-linter.yml/badge.svg)](https://github.com/ONSdigital/dis-wagtail/actions/workflows/mega-linter.yml) +[![Build Status](https://github.com/ONSdigital/dis-wagtail/actions/workflows/codeql.yml/badge.svg)](https://github.com/ONSdigital/dis-wagtail/actions/workflows/codeql.yml) + +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +[![Linting: Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) +[![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/) +[![poetry-managed](https://img.shields.io/badge/poetry-managed-blue)](https://python-poetry.org/) +[![License - MIT](https://img.shields.io/badge/licence%20-MIT-1ac403.svg)](https://github.com/ONSdigital/dis-wagtail/blob/main/LICENSE) + +The Django Wagtail CMS for managing and publishing content for the Office for National Statistics (ONS) + +--- + +## Table of Contents + +[//]: # (:TODO: Enable link checking once https://github.com/tcort/markdown-link-check/issues/250 is resolved.) + +- [Getting Started](#getting-started) + - [Pre-requisites](#pre-requisites) + - [Installation](#installation) +- [Development](#development) + - [Run Tests with Coverage](#run-tests-with-coverage) + - [Linting and Formatting](#linting-and-formatting) +- [Contributing](#contributing) +- [License](#license) + + +## Getting Started + +To get a local copy up and running, follow these simple steps. + +### Pre-requisites + +Ensure you have the following installed: + +1. **Python**: Version specified in `.python-version`. We recommend using [pyenv](https://github.com/pyenv/pyenv) for + managing Python versions. +2. **[Poetry](https://python-poetry.org/)**: This is used to manage package dependencies and virtual + environments. +3. **[Docker](https://docs.docker.com/engine/install/)** +4. **Operation System**: Ubuntu/MacOS + +### Installation + +1. Clone the repository and install the required dependencies. + + ```bash + git clone https://github.com/ONSdigital/dis-wagtail.git + ``` + +2. Install dependencies + + [Poetry](https://python-poetry.org/) is used to manage dependencies in this project. For more information, read + the [Poetry documentation](https://python-poetry.org/). + + To install all dependencies, including development dependencies, run: + + ```bash + make install-dev + ``` + + To install only production dependencies, run: + + ```bash + make install + ``` + +3. Run the application + + ```bash + make run + ``` + +## Development + +Get started with development by running the following commands. +Before proceeding, make sure you have the development dependencies installed using the `make install-dev` command. + +A Makefile is provided to simplify common development tasks. To view all available commands, run: + +```bash +make +``` + +### Run Tests with Coverage + +The unit tests are written using the [pytest](https://docs.pytest.org/en/stable/) framework. To run the tests and check +coverage, run: + +```bash +make test +``` + +### Linting and Formatting + +Various tools are used to lint and format the code in this project. + +#### Python + +The project uses [Ruff](https://github.com/astral-sh/ruff), [pylint](https://pylint.pycqa.org/en/latest/index.html) +and [black](https://black.readthedocs.io/en/stable/) for linting and formatting of the Python code. + +The tools are configured using the `pyproject.toml` file. + +To lint the Python code, run: + +```bash +make lint +``` + +To auto-format the Python code, and correct fixable linting issues, run: + +```bash +make format +``` + +#### MegaLinter (Lint/Format non-python files) + +[MegaLinter](https://github.com/oxsecurity/megalinter) is utilised to lint the non-python files in the project. +It offers a single interface to execute a suite of linters for multiple languages and formats, ensuring adherence to +best practices and maintaining consistency across the repository without the need to install each linter individually. + +MegaLinter examines various file types and tools, including GitHub Actions, Shell scripts, Dockerfile, etc. It is +configured using the `.mega-linter.yml` file. + +To run MegaLinter, ensure you have **Docker** installed on your system. + +> Note: The initial run may take some time to download the Docker image. However, subsequent executions will be +> considerably faster due to Docker caching. :rocket: + +To start the linter and automatically rectify fixable issues, run: + +```bash +make megalint +``` + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) for details. + +## License + +See [LICENSE](LICENSE) for details. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..d40e2cd --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,76 @@ +# VULNERABILITY DISCLOSURE POLICY + +## Scope + +This disclosure policy applies only to vulnerabilities in the ONS products and services under the following conditions: + +- ‘In scope’ vulnerabilities must be original, previously unreported, and not already discovered by internal procedures. +- Volumetric vulnerabilities are not in scope - meaning that simply overwhelming a service with a high volume of + requests is not in scope. +- Reports of non-exploitable vulnerabilities, or reports indicating that our services do not fully align with 'best + practice', for example missing security headers, are not in scope. +- TLS configuration weaknesses, for example 'weak'cipher suite support or the presence of TLS1.0 support, are not in + scope. +- The policy applies to everyone, including for example the ONSstaff, third party suppliers and general users of the ONS + publicservices. + +## Reporting + +If you have discovered something you believe to be an in-scope security vulnerability, first you should check the above +details for more information about scope, then submit a report +on [this page](https://hackerone.com/52fa7bc0-5356-4c86-9f79-eeb03e1d55cc/embedded_submissions/new). In your submission, +include details of: + +- The website or page where the vulnerability can be observed. +- A brief description of the type of vulnerability, for example an 'XSS vulnerability'. + +Your report should provide a benign, non-destructive, proof of exploitation. This helps to ensure that the report can be +triaged quickly and accurately. It also reduces the likelihood of duplicate reports, or malicious exploitation of some +vulnerabilities, such as sub-domain takeovers. + +## What to expect + +After you have submitted your report, we will respond to your report within 5 working days and aim to triage your report +within 10 working days. We’ll also keep you informed about our progress throughout the process via HackerOne if you have +registered for an account. + +Priority for bug fixes or mitigations are assessed by looking at the impact severity and exploit complexity. +Vulnerability reports might take some time to triage or address. You are welcome to enquire onthe status of the process +but should avoid doing so more than once every 14 days. The reason is to allow our teams to focus on the reports as much +as possible. + +When the reported vulnerability is resolved, or remediation work is scheduled, the Vulnerability Disclosure Team will +notify you, and invite you to confirm that the solution covers the vulnerability adequately. + +## Legalities + +This policy is designed to be compatible with common vulnerability disclosure good practice. It does not give you +permission to act in any manner that is inconsistent with the law, or which might cause the ONS to be in breach of any +of its legal obligations, including but not limited to: + +- The Computer Misuse Act (1990) +- The General Data Protection Regulation 2016/679 (GDPR) and the Data Protection Act 2018 +- The Copyright, Designs and Patents Act (1988) +- The Official Secrets Act (1989) + +The ONS affirms that it will not seek prosecution of any security researcher who reports any security vulnerability on a +ONS service or system, where the researcher has acted in good faith and in accordance with this disclosure policy. + +## Guidance + +You must NOT: + +- Access unnecessary amounts of data. For example, 2 or 3 records is enough to demonstrate most vulnerabilities, such + asan enumeration or direct object reference vulnerability. +- Use high-intensity invasive or destructive technical security scanning tools to find vulnerabilities. +- Violate the privacy of the ONS users, staff, contractors, services or systems. For example, by sharing, redistributing + and/or not properly securing data retrieved from our systems or services. +- Communicate any vulnerabilities or associated details using methods not described in this policy. +- Modify data in the ONS systems or services. +- Disrupt the ONS services or systems. +- Social engineer, 'phish' or physically attack ONS staff or infrastructure. +- Disclose any vulnerabilities in the ONS systems or services to 3rd parties or the public, prior to the ONS confirming + that those vulnerabilities have been mitigated or rectified. However, this is not intended to stop you notifying a + vulnerability to 3rd parties for whom the vulnerability is directly relevant. An example would be where the + vulnerability being reported is in a 3rd party software library or framework. Details of the specific vulnerability as + it applies to the ONS must not be referenced in such reports. diff --git a/dis_wagtail/__init__.py b/dis_wagtail/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dis_wagtail/calculator.py b/dis_wagtail/calculator.py new file mode 100644 index 0000000..43c79b5 --- /dev/null +++ b/dis_wagtail/calculator.py @@ -0,0 +1,76 @@ +"""A simple python module for demonstration purposes, replace it with your own code.""" + +from dataclasses import dataclass, field +from decimal import Decimal + + +@dataclass +class Calculator: + """A calculator class for basic arithmetic operations with high precision Decimal numbers. + The class keeps track of a cumulative total that is updated with each operation. + """ + + _cumulative_total: Decimal = field(default=Decimal("0"), init=False, repr=False) + + @property + def cumulative_total( + self, + ) -> Decimal: + return self._cumulative_total + + def reset_cumulative_total( + self, + ) -> None: + self._cumulative_total = Decimal("0") + + def add(self, number: Decimal) -> Decimal: + """Adds a Decimal value to the cumulative total. + + Args: + number: A Decimal value to be added to the cumulative total. + + Returns: + The cumulative total after the addition. + """ + self._cumulative_total += number + return self._cumulative_total + + def subtract(self, number: Decimal) -> Decimal: + """Subtracts a Decimal value from the cumulative total. + + Args: + number: The Decimal value to be subtracted. + + Returns: + The cumulative total after the subtraction as a Decimal. + + """ + self._cumulative_total -= number + return self._cumulative_total + + def multiply(self, number: Decimal) -> Decimal: + """Multiplies the cumulative total by a Decimal value. + + Args: + number: The Decimal value to multiply the cumulative total by. + + Returns: + The product of the provided factors as a Decimal. + """ + self._cumulative_total *= number + return self._cumulative_total + + def divide(self, divisor: Decimal) -> Decimal: + """Divides the cumulative total by a Decimal value. + + Args: + divisor: The Decimal value to divide the cumulative total by. + + Returns: + The cumulative total after the division as a Decimal. + """ + if divisor == Decimal("0"): + raise ValueError("Cannot divide by zero.") + + self._cumulative_total /= divisor + return self._cumulative_total diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..1310776 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,490 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "astroid" +version = "3.1.0" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "black" +version = "24.4.1" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f7749fd0d97ff9415975a1432fac7df89bf13c3833cea079e55fa004d5f28c0"}, + {file = "black-24.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859f3cc5d2051adadf8fd504a01e02b0fd866d7549fff54bc9202d524d2e8bd7"}, + {file = "black-24.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59271c9c29dfa97f7fda51f56c7809b3f78e72fd8d2205189bbd23022a0618b6"}, + {file = "black-24.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:5ed9c34cba223149b5a0144951a0f33d65507cf82c5449cb3c35fe4b515fea9a"}, + {file = "black-24.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9dae3ae59d6f2dc93700fd5034a3115434686e66fd6e63d4dcaa48d19880f2b0"}, + {file = "black-24.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5f8698974a81af83283eb47644f2711b5261138d6d9180c863fce673cbe04b13"}, + {file = "black-24.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f404b6e77043b23d0321fb7772522b876b6de737ad3cb97d6b156638d68ce81"}, + {file = "black-24.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:c94e52b766477bdcd010b872ba0714d5458536dc9d0734eff6583ba7266ffd89"}, + {file = "black-24.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:962d9e953872cdb83b97bb737ad47244ce2938054dc946685a4cad98520dab38"}, + {file = "black-24.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b1d8e3b2486b7dd522b1ab2ba1ec4907f0aa8f5e10a33c4271fb331d1d10b70c"}, + {file = "black-24.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed77e214b785148f57e43ca425b6e0850165144aa727d66ac604e56a70bb7825"}, + {file = "black-24.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:4ef4764437d7eba8386689cd06e1fb5341ee0ae2e9e22582b21178782de7ed94"}, + {file = "black-24.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:92b183f8eef5baf7b20a513abcf982ad616f544f593f6688bb2850d2982911f1"}, + {file = "black-24.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:945abd7b3572add997757c94295bb3e73c6ffaf3366b1f26cb2356a4bffd1dc3"}, + {file = "black-24.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db5154b9e5b478031371d8bc41ff37b33855fa223a6cfba456c9b73fb96f77d4"}, + {file = "black-24.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:afc84c33c1a9aaf3d73140cee776b4ddf73ff429ffe6b7c56dc1c9c10725856d"}, + {file = "black-24.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0889f4eb8b3bdf8b189e41a71cf0dbb8141a98346cd1a2695dea5995d416e940"}, + {file = "black-24.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5bb0143f175db45a55227eefd63e90849d96c266330ba31719e9667d0d5ec3b9"}, + {file = "black-24.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:713a04a78e78f28ef7e8df7a16fe075670ea164860fcef3885e4f3dffc0184b3"}, + {file = "black-24.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:171959bc879637a8cdbc53dc3fddae2a83e151937a28cf605fd175ce61e0e94a"}, + {file = "black-24.4.1-py3-none-any.whl", hash = "sha256:ecbab810604fe02c70b3a08afd39beb599f7cc9afd13e81f5336014133b4fe35"}, + {file = "black-24.4.1.tar.gz", hash = "sha256:5241612dc8cad5b6fd47432b8bd04db80e07cfbc53bb69e9ae18985063bcb8dd"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.5.0" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:432949a32c3e3f820af808db1833d6d1631664d53dd3ce487aa25d574e18ad1c"}, + {file = "coverage-7.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bd7065249703cbeb6d4ce679c734bef0ee69baa7bff9724361ada04a15b7e3b"}, + {file = "coverage-7.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbfe6389c5522b99768a93d89aca52ef92310a96b99782973b9d11e80511f932"}, + {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39793731182c4be939b4be0cdecde074b833f6171313cf53481f869937129ed3"}, + {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a5dbe1ba1bf38d6c63b6d2c42132d45cbee6d9f0c51b52c59aa4afba057517"}, + {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:357754dcdfd811462a725e7501a9b4556388e8ecf66e79df6f4b988fa3d0b39a"}, + {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a81eb64feded34f40c8986869a2f764f0fe2db58c0530d3a4afbcde50f314880"}, + {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51431d0abbed3a868e967f8257c5faf283d41ec882f58413cf295a389bb22e58"}, + {file = "coverage-7.5.0-cp310-cp310-win32.whl", hash = "sha256:f609ebcb0242d84b7adeee2b06c11a2ddaec5464d21888b2c8255f5fd6a98ae4"}, + {file = "coverage-7.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6782cd6216fab5a83216cc39f13ebe30adfac2fa72688c5a4d8d180cd52e8f6a"}, + {file = "coverage-7.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e768d870801f68c74c2b669fc909839660180c366501d4cc4b87efd6b0eee375"}, + {file = "coverage-7.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84921b10aeb2dd453247fd10de22907984eaf80901b578a5cf0bb1e279a587cb"}, + {file = "coverage-7.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710c62b6e35a9a766b99b15cdc56d5aeda0914edae8bb467e9c355f75d14ee95"}, + {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c379cdd3efc0658e652a14112d51a7668f6bfca7445c5a10dee7eabecabba19d"}, + {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea9d3ca80bcf17edb2c08a4704259dadac196fe5e9274067e7a20511fad1743"}, + {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:41327143c5b1d715f5f98a397608f90ab9ebba606ae4e6f3389c2145410c52b1"}, + {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:565b2e82d0968c977e0b0f7cbf25fd06d78d4856289abc79694c8edcce6eb2de"}, + {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cf3539007202ebfe03923128fedfdd245db5860a36810136ad95a564a2fdffff"}, + {file = "coverage-7.5.0-cp311-cp311-win32.whl", hash = "sha256:bf0b4b8d9caa8d64df838e0f8dcf68fb570c5733b726d1494b87f3da85db3a2d"}, + {file = "coverage-7.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c6384cc90e37cfb60435bbbe0488444e54b98700f727f16f64d8bfda0b84656"}, + {file = "coverage-7.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fed7a72d54bd52f4aeb6c6e951f363903bd7d70bc1cad64dd1f087980d309ab9"}, + {file = "coverage-7.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cbe6581fcff7c8e262eb574244f81f5faaea539e712a058e6707a9d272fe5b64"}, + {file = "coverage-7.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad97ec0da94b378e593ef532b980c15e377df9b9608c7c6da3506953182398af"}, + {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd4bacd62aa2f1a1627352fe68885d6ee694bdaebb16038b6e680f2924a9b2cc"}, + {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf032b6c105881f9d77fa17d9eebe0ad1f9bfb2ad25777811f97c5362aa07f2"}, + {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ba01d9ba112b55bfa4b24808ec431197bb34f09f66f7cb4fd0258ff9d3711b1"}, + {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f0bfe42523893c188e9616d853c47685e1c575fe25f737adf473d0405dcfa7eb"}, + {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a9a7ef30a1b02547c1b23fa9a5564f03c9982fc71eb2ecb7f98c96d7a0db5cf2"}, + {file = "coverage-7.5.0-cp312-cp312-win32.whl", hash = "sha256:3c2b77f295edb9fcdb6a250f83e6481c679335ca7e6e4a955e4290350f2d22a4"}, + {file = "coverage-7.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:427e1e627b0963ac02d7c8730ca6d935df10280d230508c0ba059505e9233475"}, + {file = "coverage-7.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dd88fce54abbdbf4c42fb1fea0e498973d07816f24c0e27a1ecaf91883ce69e"}, + {file = "coverage-7.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a898c11dca8f8c97b467138004a30133974aacd572818c383596f8d5b2eb04a9"}, + {file = "coverage-7.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07dfdd492d645eea1bd70fb1d6febdcf47db178b0d99161d8e4eed18e7f62fe7"}, + {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3d117890b6eee85887b1eed41eefe2e598ad6e40523d9f94c4c4b213258e4a4"}, + {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6afd2e84e7da40fe23ca588379f815fb6dbbb1b757c883935ed11647205111cb"}, + {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9960dd1891b2ddf13a7fe45339cd59ecee3abb6b8326d8b932d0c5da208104f"}, + {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ced268e82af993d7801a9db2dbc1d2322e786c5dc76295d8e89473d46c6b84d4"}, + {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7c211f25777746d468d76f11719e64acb40eed410d81c26cefac641975beb88"}, + {file = "coverage-7.5.0-cp38-cp38-win32.whl", hash = "sha256:262fffc1f6c1a26125d5d573e1ec379285a3723363f3bd9c83923c9593a2ac25"}, + {file = "coverage-7.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:eed462b4541c540d63ab57b3fc69e7d8c84d5957668854ee4e408b50e92ce26a"}, + {file = "coverage-7.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0194d654e360b3e6cc9b774e83235bae6b9b2cac3be09040880bb0e8a88f4a1"}, + {file = "coverage-7.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33c020d3322662e74bc507fb11488773a96894aa82a622c35a5a28673c0c26f5"}, + {file = "coverage-7.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbdf2cae14a06827bec50bd58e49249452d211d9caddd8bd80e35b53cb04631"}, + {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3235d7c781232e525b0761730e052388a01548bd7f67d0067a253887c6e8df46"}, + {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2de4e546f0ec4b2787d625e0b16b78e99c3e21bc1722b4977c0dddf11ca84e"}, + {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0e206259b73af35c4ec1319fd04003776e11e859936658cb6ceffdeba0f5be"}, + {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2055c4fb9a6ff624253d432aa471a37202cd8f458c033d6d989be4499aed037b"}, + {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075299460948cd12722a970c7eae43d25d37989da682997687b34ae6b87c0ef0"}, + {file = "coverage-7.5.0-cp39-cp39-win32.whl", hash = "sha256:280132aada3bc2f0fac939a5771db4fbb84f245cb35b94fae4994d4c1f80dae7"}, + {file = "coverage-7.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:c58536f6892559e030e6924896a44098bc1290663ea12532c78cef71d0df8493"}, + {file = "coverage-7.5.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:2b57780b51084d5223eee7b59f0d4911c31c16ee5aa12737c7a02455829ff067"}, + {file = "coverage-7.5.0.tar.gz", hash = "sha256:cf62d17310f34084c59c01e027259076479128d11e4661bb6c9acb38c5e19bb8"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "exceptiongroup" +version = "1.2.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, + {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "execnet" +version = "2.1.1" +description = "execnet: rapid multi-Python deployment" +optional = false +python-versions = ">=3.8" +files = [ + {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, + {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, +] + +[package.extras] +testing = ["hatch", "pre-commit", "pytest", "tox"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mypy" +version = "1.10.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"}, + {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"}, + {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"}, + {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"}, + {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"}, + {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"}, + {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"}, + {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"}, + {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"}, + {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"}, + {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"}, + {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"}, + {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"}, + {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"}, + {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"}, + {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"}, + {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"}, + {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"}, + {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"}, + {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"}, + {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"}, + {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"}, + {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"}, + {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"}, + {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"}, + {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"}, + {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, + {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pylint" +version = "3.1.0" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, +] + +[package.dependencies] +astroid = ">=3.1.0,<=3.2.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, +] +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "5.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "pytest-xdist" +version = "3.5.0" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-xdist-3.5.0.tar.gz", hash = "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a"}, + {file = "pytest_xdist-3.5.0-py3-none-any.whl", hash = "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24"}, +] + +[package.dependencies] +execnet = ">=1.1" +pytest = ">=6.2.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + +[[package]] +name = "ruff" +version = "0.4.2" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.4.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8d14dc8953f8af7e003a485ef560bbefa5f8cc1ad994eebb5b12136049bbccc5"}, + {file = "ruff-0.4.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:24016ed18db3dc9786af103ff49c03bdf408ea253f3cb9e3638f39ac9cf2d483"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2e06459042ac841ed510196c350ba35a9b24a643e23db60d79b2db92af0c2b"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3afabaf7ba8e9c485a14ad8f4122feff6b2b93cc53cd4dad2fd24ae35112d5c5"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:799eb468ea6bc54b95527143a4ceaf970d5aa3613050c6cff54c85fda3fde480"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ec4ba9436a51527fb6931a8839af4c36a5481f8c19e8f5e42c2f7ad3a49f5069"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6a2243f8f434e487c2a010c7252150b1fdf019035130f41b77626f5655c9ca22"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8772130a063f3eebdf7095da00c0b9898bd1774c43b336272c3e98667d4fb8fa"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ab165ef5d72392b4ebb85a8b0fbd321f69832a632e07a74794c0e598e7a8376"}, + {file = "ruff-0.4.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1f32cadf44c2020e75e0c56c3408ed1d32c024766bd41aedef92aa3ca28eef68"}, + {file = "ruff-0.4.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:22e306bf15e09af45ca812bc42fa59b628646fa7c26072555f278994890bc7ac"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + +[[package]] +name = "typing-extensions" +version = "4.11.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.10" +content-hash = "e48340787b27b71f6c04d826d7aab3ebdb690f9f1c5e8bc93158380b328c88c5" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..79930c9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,92 @@ +[tool.poetry] +name = "dis-wagtail" +version = "0.1.0" +description = "The Django Wagtail CMS for managing and publishing content for the Office for National Statistics (ONS)" +authors = ["ONSdigital"] +license = "MIT" +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.10" + +[tool.poetry.group.dev.dependencies] +# :TODO: Remove pylint when ruff supports all pylint rules +# :TODO: Remove black when ruff supports all black rules +pylint = "^3.1.0" +black = "^24.4.1" +pytest = "^8.1.1" +pytest-xdist = "^3.5.0" +ruff = "^0.4.2" +pytest-cov = "^5.0.0" +mypy = "^1.10.0" + +[tool.black] +line-length = 120 + +[tool.ruff] +line-length = 120 +indent-width = 4 + +[tool.ruff.lint] +select = [ + # Enabling ALL is not recommended it will implicitly enable new rules after upgrade. + # "ALL", + + # Ruff rules: https://docs.astral.sh/ruff/rules/ + + "E", # pycodestyle erros + "W", # pycodestyle warnings + "F", # Pyflakes + "UP", # pyupgrade + "I", # isort + "B", # flake8-bugbear + "SIM", # flake8-simplify + "C4", # flake8-comprehensions + "S", # flake8-bandit + "D", # pydocstyle - Enforce existing docstrings only + "C90", # mccabe + "RUF", # Ruff specific rules + + # PL - Pylint is only partially supported, we also use the pylint tool to catch all the rules. + # It is enabled here to take advantage of the ruff's speed. + "PL", +] + +ignore = [ + # Conflicts with google docstring style + "D205", + # Allow missing docstring, remove to enforce docstrings across the board + "D100", "D101", "D102", "D103", "D104", "D105", "D106", "D107", + # Disable line length check as it is handled by black + # :TODO: Remove E501 when ruff supports all black rules + "E501", + # indentation contains tabs + "W191", +] + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.lint.per-file-ignores] +"tests/*" = [ + # Allow use of assert statements in tests + "S101", +] + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" + +[tool.mypy] +# Global mypy options +no_implicit_optional = "True" +ignore_missing_imports = "True" +warn_unused_configs = "True" +warn_no_return = "False" +warn_unused_ignores = "True" +warn_return_any = "True" +warn_redundant_casts = "True" +disallow_untyped_defs = "True" +disallow_untyped_calls = "True" +disallow_incomplete_defs = "True" +strict_equality = "True" diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py new file mode 100644 index 0000000..8923622 --- /dev/null +++ b/tests/unit/conftest.py @@ -0,0 +1,10 @@ +import pytest + +from dis_wagtail.calculator import Calculator + + +@pytest.fixture() +def calculator(): + # Create a new instance of the Calculator class for each test session. + yield Calculator() + # Clean up after the test session is complete. diff --git a/tests/unit/test_calculator.py b/tests/unit/test_calculator.py new file mode 100644 index 0000000..013731d --- /dev/null +++ b/tests/unit/test_calculator.py @@ -0,0 +1,57 @@ +from decimal import Decimal + +import pytest + + +def test_add(calculator): + assert calculator.add(Decimal("5")) == Decimal("5") + assert calculator.add(Decimal("10")) == Decimal("15") + + assert calculator.cumulative_total == Decimal("15") + + +def test_subtract(calculator): + assert calculator.subtract(Decimal("5")) == Decimal("-5") + assert calculator.subtract(Decimal("10")) == Decimal("-15") + + assert calculator.cumulative_total == Decimal("-15") + + +def test_multiply(calculator): + calculator.add(Decimal("1")) + assert calculator.multiply(Decimal("10")) == Decimal("10") + assert calculator.multiply(Decimal("2")) == Decimal("20") + + assert calculator.cumulative_total == Decimal("20") + + +def test_divide(calculator): + calculator.add(Decimal("6")) + assert calculator.divide(Decimal("2")) == Decimal("3") + assert calculator.divide(Decimal("3")) == Decimal("1") + + assert calculator.cumulative_total == Decimal("1") + + +def test_divide_by_zero(calculator): + with pytest.raises(ValueError): + calculator.divide(Decimal("0")) + + +def test_multiple_operations(calculator): + assert calculator.cumulative_total == Decimal("0") + + calculator.add(Decimal("15")) # 15 + calculator.subtract(Decimal("5")) # 10 + calculator.multiply(Decimal("2")) # 20 + calculator.divide(Decimal("4")) # 5 + + assert calculator.cumulative_total == Decimal("5") + + +def test_reset_cumulative_total(calculator): + calculator.add(Decimal("15")) + assert calculator.cumulative_total == Decimal("15") + + calculator.reset_cumulative_total() + assert calculator.cumulative_total == Decimal("0")