diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index dca8b1e3..8cfa3e73 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -13,14 +13,12 @@ RUN if [ -f "/tmp/conda-tmp/environment.yml" ]; then umask 0002 && /opt/conda/bi && rm -rf /tmp/conda-tmp RUN conda install -y python=3.8 \ - && python3.8 -m pip install --upgrade pip tox - -COPY requirements.test.txt /tmp/ -RUN python3.8 -m pip install -r /tmp/requirements.test.txt + && python3.8 -m pip install --upgrade pip \ + && python3.8 -m pip install \ + pytest \ + pytest-cov \ + flit \ + pre-commit -COPY requirements.txt /tmp/ -RUN python3.8 -m pip install -r /tmp/requirements.txt - -# [Optional] Uncomment this section to install additional OS packages. -# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ -# && apt-get -y install --no-install-recommends +COPY .pre-commit-config.yaml . +RUN git init . && pre-commit install-hooks diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 88e9771b..acf32a4e 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,16 +2,15 @@ // https://github.com/microsoft/vscode-dev-containers/tree/v0.222.0/containers/python-3-miniconda { "name": "Miniconda (Python 3)", - "build": { + "build": { "context": "..", "dockerfile": "Dockerfile", "args": { "NODE_VERSION": "none" } }, - // Set *default* container specific settings.json values on container create. - "settings": { + "settings": { "python.defaultInterpreterPath": "/opt/conda/bin/python", "python.linting.enabled": true, "python.linting.pylintEnabled": true, @@ -25,19 +24,20 @@ "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint" }, - // Add the IDs of extensions you want installed when the container is created. "extensions": [ "ms-python.python", - "ms-python.vscode-pylance" + "ms-python.vscode-pylance", + "ms-python.pylint", + "ms-python.black-formatter", + "ms-vsliveshare.vsliveshare", + "ryanluker.vscode-coverage-gutters", + "bungcip.better-toml" ], - // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], - // Use 'postCreateCommand' to run commands after the container is created. // "postCreateCommand": "python --version", - // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. "remoteUser": "vscode", "features": { diff --git a/.devcontainer/noop.txt b/.devcontainer/noop.txt index 82122ed2..6c7539b6 100644 --- a/.devcontainer/noop.txt +++ b/.devcontainer/noop.txt @@ -1,3 +1,3 @@ This file is copied into the container along with environment.yml* from the -parent folder. This is done to prevent the Dockerfile COPY instruction from +parent folder. This is done to prevent the Dockerfile COPY instruction from failing if no environment.yml is found. diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..02caadfe --- /dev/null +++ b/.flake8 @@ -0,0 +1,16 @@ +[flake8] +max-line-length = 120 +select = "F,E,W,B,B901,B902,B903" +exclude = [ + ".eggs", + ".git", + ".tox", + "nssm", + "obj", + "out", + "packages", + "pywin32", + "tests", + "swagger_client" +] +ignore = "E722,B001,W503,E203" diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 00000000..6e19148b --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,31 @@ +name: Python CI +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + release: + types: [created] + workflow_dispatch: + +jobs: + tooling: + runs-on: ubuntu-latest + strategy: + matrix: + tools: ['black', 'bandit', 'pylint', 'pyright', 'flake8'] + include: + - tools: pytest + args: -m not integration and not gpu + - tools: pytest + args: -m integration + steps: + - uses: actions/checkout@v2 + - name: ${{ matrix.tools }} + uses: dciborow/pyaction@0.0.28 + with: + ${{ matrix.tools }}: true + args: ${{ matrix.args }} + + publish: + uses: ./.github/workflows/publish.yml diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml deleted file mode 100644 index 8b058d15..00000000 --- a/.github/workflows/black.yml +++ /dev/null @@ -1,31 +0,0 @@ -# This is a basic workflow to help you get started with Actions - -name: Black - -# Controls when the workflow will run -on: - # Triggers the workflow on push or pull request events but only for the main branch - push: - branches: [ main ] - pull_request: - branches: [ main ] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: -jobs: - linter_name: - name: runner / black formatter - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Check files using the black formatter - uses: rickstaa/action-black@v1 - id: action_black - with: - black_args: "." - - name: Annotate diff changes using reviewdog - if: steps.action_black.outputs.is_formatted == 'true' - uses: reviewdog/action-suggester@v1 - with: - tool_name: blackfmt - fail_on_error: true diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 4b418ea7..00000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: wemake-python-styleguide - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - - workflow_dispatch: -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: wemake-python-styleguide - uses: wemake-services/wemake-python-styleguide@0.15.3 - env: - GITHUB_TOKEN: ${{ github.token }} - with: - path: ${{ secrets.PACKAGE }} - reporter: github-pr-review diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml deleted file mode 100644 index 1cd1474a..00000000 --- a/.github/workflows/mypy.yml +++ /dev/null @@ -1,27 +0,0 @@ -# This is a basic workflow to help you get started with Actions - -name: MyPy - -# Controls when the workflow will run -on: - # Triggers the workflow on push or pull request events but only for the main branch - push: - branches: [ main ] - pull_request: - branches: [ main ] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: -jobs: - linter_name: - name: runner / black formatter - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Run mypy with reviewdog - # You may pin to the exact commit or the version. - # uses: tsuyoshicho/action-mypy@2160e947d397ac0be7f02c911c3a5bea3f498575 - uses: tsuyoshicho/action-mypy@v3.1.0 - with: - reporter: github-pr-review - workdir: ${{ secrets.PACKAGE }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..96a7fb06 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,33 @@ +name: Python Publish Workflow +on: + workflow_call: + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - if: ${{ github.event_name == 'release' && secrets.PYPI_PASSWORD != '' }} + name: Publish Release to PyPi + uses: dciborow/pyaction@0.0.28 + with: + pypi_publish: true + pypi_password: ${{ secrets.PYPI_PASSWORD }} + + - if: ${{ github.event_name == 'push' && secrets.PYPI_PASSWORD != '' }} + name: Publish RC to PyPi + uses: dciborow/pyaction@0.0.28 + with: + pypi_publish: true + pypi_password: ${{ secrets.PYPI_PASSWORD }} + version_suffix: -rc${{ github.run_number }}-post${{ github.run_attempt }} + + - if: ${{ github.event_name == 'pull_request' && secrets.TEST_PYPI_PASSWORD != '' }} + name: Publish Snapshot to TestPyPi + uses: dciborow/pyaction@0.0.28 + with: + pypi_publish: true + pypi_password: ${{ secrets.TEST_PYPI_PASSWORD }} + pypi_repo: testpypi + version_suffix: -post${{ github.run_number }}-dev${{ github.run_attempt }} diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml deleted file mode 100644 index 46b187f2..00000000 --- a/.github/workflows/pylint.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: PyLint -on: - # Triggers the workflow on push or pull request events but only for the main branch - push: - branches: [ main ] - pull_request: - branches: [ main ] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -jobs: - pylint: - name: runner / pylint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: dciborow/action-pylint@0.0.3 - with: - github_token: ${{ secrets.github_token }} - # Change reviewdog reporter if you need [github-pr-check,github-check,github-pr-review]. - reporter: github-pr-review - # Change reporter level if you need. - # GitHub Status Check won't become failure with warning. - workdir: ${{ secrets.PACKAGE }} - level: warning diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml deleted file mode 100644 index e1ba116c..00000000 --- a/.github/workflows/pypi-publish.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Upload Python Package - -on: - release: - types: [created] - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Setup Python - uses: actions/setup-python@v2.2.2 - with: - python-version: 3.7 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install setuptools wheel twine - python setup.py sdist bdist_wheel - - name: Publish package - uses: pypa/gh-action-pypi-publish@release/v1 - with: - user: __token__ - password: ${{ secrets.PYPI_PASSWORD }} - - name: Install dependencies - run: | - python3.7 -m pip install --upgrade pip - python3.7 -m pip install virtualenv pip-tools - pip install ${{ secrets.PACKAGE }} diff --git a/.github/workflows/pypi-test-publish.yml b/.github/workflows/pypi-test-publish.yml deleted file mode 100644 index d3f56cd8..00000000 --- a/.github/workflows/pypi-test-publish.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Test Upload Python Package - -on: - workflow_dispatch - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Setup Python - uses: actions/setup-python@v2.2.2 - with: - python-version: 3.7 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install setuptools wheel twine - python setup.py sdist bdist_wheel - - name: Publish package - uses: pypa/gh-action-pypi-publish@release/v1 - with: - user: __token__ - password: ${{ secrets.TEST_PYPI_PASSWORD }} - repository_url: https://test.pypi.org/legacy/ - - name: Install dependencies - run: | - python3.7 -m pip install --upgrade pip - python3.7 -m pip install virtualenv pip-tools - pip install --extra-index-url https://test.pypi.org/simple ${{ secrets.PACKAGE }} diff --git a/.github/workflows/pyright.yml b/.github/workflows/pyright.yml deleted file mode 100644 index 697cc019..00000000 --- a/.github/workflows/pyright.yml +++ /dev/null @@ -1,27 +0,0 @@ -# This is a basic workflow to help you get started with Actions - -name: pyright - -# Controls when the workflow will run -on: - # Triggers the workflow on push or pull request events but only for the main branch - push: - branches: [ main ] - pull_request: - branches: [ main ] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: -jobs: - linter_name: - name: runner / black formatter - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: jordemort/action-pyright@v1 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} # You need this - reporter: github-pr-review # Change reporter. - - uses: ricardochaves/python-lint@v1.4.0 - with: - python-root-list: ${{ secrets.PACKAGE }} diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml deleted file mode 100644 index 4fdbd0ba..00000000 --- a/.github/workflows/pytest.yml +++ /dev/null @@ -1,32 +0,0 @@ -# This is a basic workflow to help you get started with Actions - -name: PyTest - -# Controls when the workflow will run -on: - # Triggers the workflow on push or pull request events but only for the main branch - push: - branches: [ main ] - pull_request: - branches: [ main ] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v1 - with: - python-version: '3.x' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pytest - - name: Test dependencies - run: | - pip install -e .[all] - pytest tests diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..e7ceb545 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,60 @@ +ci: + autoupdate_commit_msg: "chore: update pre-commit hooks" + autofix_commit_msg: "style: pre-commit fixes" + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.1.0 + hooks: + - id: check-added-large-files + - id: check-case-conflict + - id: check-merge-conflict + - id: check-symlinks + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + - id: mixed-line-ending + - id: requirements-txt-fixer + - id: trailing-whitespace + + - repo: https://github.com/PyCQA/isort + rev: 5.10.1 + hooks: + - id: isort + args: ["-a", "from __future__ import annotations"] + + - repo: https://github.com/asottile/pyupgrade + rev: v2.31.0 + hooks: + - id: pyupgrade + args: [--py37-plus] + + - repo: https://github.com/hadialqattan/pycln + rev: v1.2.5 + hooks: + - id: pycln + args: [--config=pyproject.toml] + stages: [manual] + + - repo: https://github.com/codespell-project/codespell + rev: v2.1.0 + hooks: + - id: codespell + + - repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.9.0 + hooks: + - id: python-check-blanket-noqa + - id: python-check-blanket-type-ignore + - id: python-no-log-warn + - id: python-no-eval + - id: python-use-type-annotations + - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal + + - repo: https://github.com/mgedmin/check-manifest + rev: "0.47" + hooks: + - id: check-manifest + stages: [manual] diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index 816ca46d..00000000 --- a/.pylintrc +++ /dev/null @@ -1,158 +0,0 @@ -[MASTER] -extension-pkg-whitelist=numpy,torch,cv2,pyodbc,pydantic,ciso8601,netcdf4,scipy -ignore=CVS -ignore-patterns=test.*?py,conftest.py -init-hook='import sys; sys.setrecursionlimit(8 * sys.getrecursionlimit())' -jobs=0 -limit-inference-results=100 -persistent=yes -suggestion-mode=yes -unsafe-load-any-extension=no -[MESSAGES CONTROL] -confidence= -enable=c-extension-no-member - -[REPORTS] -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) -output-format=text -reports=no -score=yes -[REFACTORING] -max-nested-blocks=5 -never-returning-functions=sys.exit - -[BASIC] -argument-naming-style=snake_case -attr-naming-style=snake_case -bad-names=foo, - bar, - baz, - toto, - tutu, - tata -class-attribute-naming-style=any -class-naming-style=PascalCase -const-naming-style=UPPER_CASE -docstring-min-length=-1 -function-naming-style=snake_case -good-names=i, - j, - k, - ex, - Run, - _, - df, - n, - N, - t, - T, - ax -include-naming-hint=yes -inlinevar-naming-style=any -method-naming-style=snake_case -module-naming-style=any -name-group= -no-docstring-rgx=^_ -property-classes=abc.abstractproperty -variable-naming-style=snake_case - -[FORMAT] -expected-line-ending-format= -ignore-long-lines=^\s*(# )?.*['"]?? -indent-after-paren=4 -indent-string=' ' -max-line-length=120 -max-module-lines=1000 -no-space-check=trailing-comma, - dict-separator -single-line-class-stmt=no -single-line-if-stmt=no - -[LOGGING] -logging-format-style=old -logging-modules=logging - -[MISCELLANEOUS] -notes=FIXME, - XXX, - TODO - -[SIMILARITIES] -ignore-comments=yes -ignore-docstrings=yes -ignore-imports=yes -min-similarity-lines=7 - -[SPELLING] -max-spelling-suggestions=4 -spelling-dict= -spelling-ignore-words= -spelling-private-dict-file= -spelling-store-unknown-words=no - -[STRING] -check-str-concat-over-line-jumps=no - -[TYPECHECK] -contextmanager-decorators=contextlib.contextmanager -generated-members=numpy.*,np.*,pyspark.sql.functions,collect_list -ignore-mixin-members=yes -ignore-none=yes -ignore-on-opaque-inference=yes -ignored-classes=optparse.Values,thread._local,_thread._local,numpy,torch,swagger_client -ignored-modules=numpy,torch,swagger_client,netCDF4,scipy -missing-member-hint=yes -missing-member-hint-distance=1 -missing-member-max-choices=1 -signature-mutators= - -[VARIABLES] -additional-builtins=dbutils -allow-global-unused-variables=yes -callbacks=cb_, - _cb -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ -ignored-argument-names=_.*|^ignored_|^unused_ -init-import=no -redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io - -[CLASSES] -defining-attr-methods=__init__, - __new__, - setUp, - __post_init__ -exclude-protected=_asdict, - _fields, - _replace, - _source, - _make -valid-classmethod-first-arg=cls -valid-metaclass-classmethod-first-arg=cls - -[DESIGN] -max-args=5 -max-attributes=7 -max-bool-expr=5 -max-branches=12 -max-locals=15 -max-parents=7 -max-public-methods=20 -max-returns=6 -max-statements=50 -min-public-methods=2 - -[IMPORTS] -allow-any-import-level= -allow-wildcard-with-all=no -analyse-fallback-blocks=no -deprecated-modules=optparse,tkinter.tix -ext-import-graph= -import-graph= -int-import-graph= -known-standard-library= -known-third-party=enchant, azureiai-logistics-inventoryplanning -preferred-modules= - -[EXCEPTIONS] -overgeneral-exceptions=BaseException, - Exception diff --git a/.pypirc b/.pypirc new file mode 100644 index 00000000..52d2dd97 --- /dev/null +++ b/.pypirc @@ -0,0 +1,10 @@ +[distutils] +index-servers = + pypi + testpypi + +[pypi] +repository = https://upload.pypi.org/legacy/ + +[testpypi] +repository = https://test.pypi.org/legacy/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..8c11db2e --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "ms-python.black-formatter", + "ms-python.pylint" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..5ceebf33 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "python.testing.pytestArgs": [], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true +} diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 8ab1d298..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -include requirements.txt -include requirements.test.txt diff --git a/README.md b/README.md index 5cd7cecf..dcd90ba6 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,95 @@ -# Project +# Python Project Template -> This repo has been populated by an initial template to help get you started. Please -> make sure to update the content to build a great experience for community-building. +This project follows the Python Standards declared in [PEP 621](https://peps.python.org/pep-0621/). +This uses a pyproject.yaml to configuration the project. In this example, [flit](https://pypi.org/project/flit/) is used to simplify the build process, and publish to pypi. -As the maintainer of this project, please make a few updates: +## Project Organization + +- .devcontainer - This directory contains required files for creating a [Codespace](https://github.com/features/codespaces). +- .github + - workflows - Contains GitHub Actions used for building, testing and publishing. + - publish-test.yml - Publish wheels to [https://test.pypi.org/](https://test.pypi.org/) + - publish.yml - Publish wheels to [https://pypi.org/](https://pypi.org/) + - pull-request.yml - Build and Test pull requests before commiting to main. + - template-sync.yml - Update GitHub Repo with enhancments to base template +- docs - collect documents (default format .md) +- src - place new source code here + - python_package - sample package (this can be deleted when creating a new repository) +- tests - contains Python based test cases to validation src code +- .pre-commit-config.yaml - Contains various pre-check fixes for Python +- .templateversionrc - used to track template version usage. +- MANIFEST.in - Declares additional files to include in Python whl +- pyproject.toml - Python Project Declaration +- ws.code-workspace - Recommended configurations for [Visual Studio Code](https://code.visualstudio.com/) + +## pyproject.toml + +The following sections are defined in the configuration toml. + +- build-system +- project + - optional-dependencies + - urls +- tool + - bandit + - coverage + - run + - report + - pyright + - pytest + - tox + - pylint + - MESSAGES CONTROL + - REPORTS + - REFACTORING + - BASIC + - FORMAT + - LOGGING + - MISCELLANEOUS + - SIMILARITIES + - SPELLING + - STRING + - TYPECHECK + - VARIABLES + - CLASSES + - DESIGN + - IMPORTS + - EXCEPTIONS + +### build-system +TODO: add info on flit configuration + +### project +This section defines the project metadata, which may have been previously contained in a setup.py file. + +#### optional-dependencies +This are otpimal dependancey groups that can be installed via 'pip install .[tests]'. +One group is included for dependancies required for testing. A second group is included for PySpark based dependancies. + +### tool +This section defines the configurations for additional tools used to format, lint, type-check, and analysis Python code. + +#### bandit +Performs Security Static Analysis checks on code base. + +#### black +Auto-formats code + +#### coverage +Configures code coverage reports generatated during testing. + +#### pyright +Performs static type checking on Python. + +#### pytest +Configures various test markers used during testing. + +#### pylint +Performs Linting and Static Analysis. Any modifictions made by the auto-formater (black) are always considered correct. + +## Publish to PyPi from GitHub +In order to publish to PyPi, a repostirory secret must be created, "PYPI_PASSWORD". In order to publish to the Test PyPi, a second secret must be added, "TEST_PYPI_PASSWORD". -- Improving this README.MD file to provide a great experience -- Updating SUPPORT.MD with content about this project's support experience -- Understanding the security reporting process in SECURITY.MD -- Remove this section from the README ## Contributing @@ -26,8 +107,8 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio ## Trademarks -This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft -trademarks or logos is subject to and must follow +This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft +trademarks or logos is subject to and must follow [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies. diff --git a/SECURITY.md b/SECURITY.md index f7b89984..a050f362 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -14,7 +14,7 @@ Instead, please report them to the Microsoft Security Response Center (MSRC) at If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). -You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: @@ -38,4 +38,4 @@ We prefer all communications to be in English. Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). - \ No newline at end of file + diff --git a/SUPPORT.md b/SUPPORT.md index dc72f0e5..bcebadb8 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -10,16 +10,16 @@ # Support -## How to file issues and get help +## How to file issues and get help -This project uses GitHub Issues to track bugs and feature requests. Please search the existing -issues before filing new issues to avoid duplicates. For new issues, file your bug or +This project uses GitHub Issues to track bugs and feature requests. Please search the existing +issues before filing new issues to avoid duplicates. For new issues, file your bug or feature request as a new Issue. -For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE +For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER CHANNEL. WHERE WILL YOU HELP PEOPLE?**. -## Microsoft Support Policy +## Microsoft Support Policy Support for this **PROJECT or PRODUCT** is limited to the resources listed above. diff --git a/ai-python-package/__init__.py b/ai-python-package/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/ai-python-package/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/ai-python-package/hello_world.py b/ai-python-package/hello_world.py deleted file mode 100644 index eebf6ad6..00000000 --- a/ai-python-package/hello_world.py +++ /dev/null @@ -1,13 +0,0 @@ -def hello_world(i: int = 0) -> str: - "Doc Stringz" - print("hello world") - - -def good_night(): - print("good night") - return - - -def hello_goodbye(): - hello_world("x") - good_night() diff --git a/black.toml b/black.toml deleted file mode 100644 index 5153b0c9..00000000 --- a/black.toml +++ /dev/null @@ -1,3 +0,0 @@ -[tool.black] -line-length = 120 -fast = true diff --git a/docs/devcontainer.md b/docs/devcontainer.md new file mode 100644 index 00000000..7b7bfd2f --- /dev/null +++ b/docs/devcontainer.md @@ -0,0 +1,16 @@ +# GitHub Codespace + +The project's Codespace configuration is located in ".devcontainer". It includes the "Dockerfile" for the development container. +The project can be opened directly in a Codespace. + +## Running Unit Tests + +## Displaying Code Coverage + +## Included Extensions +### Python +### Pylance + +## Installing pre-released Extensions +### Pylint +### Black diff --git a/docs/developer.md b/docs/developer.md new file mode 100644 index 00000000..cbe350ab --- /dev/null +++ b/docs/developer.md @@ -0,0 +1,3 @@ +# Developer Guide + +## Testing Template Project diff --git a/docs/pre-commit-config.md b/docs/pre-commit-config.md new file mode 100644 index 00000000..f05fc3cb --- /dev/null +++ b/docs/pre-commit-config.md @@ -0,0 +1,6 @@ +# pre-commit-config.yaml + +Pre-commit is a Python package which can be used to create 'git' hooks which scan can prior to checkins. +The included configuration focuses on python actions which will help to prevent users from commiting code which will fail during builds. +In general, only formatting actions are automatiicaly performed. These include auto-formatting with 'black', or sorting dependacies with 'isort'. +Linting actions are left to the discretion of the user. diff --git a/docs/pyproject.md b/docs/pyproject.md new file mode 100644 index 00000000..32fa0fef --- /dev/null +++ b/docs/pyproject.md @@ -0,0 +1,9 @@ +# pypyroject.toml + +The pyproject.toml is the main configuration file used for the Python project. +It contains configurations for building, linting, testing, and publishing the Python package. + +The pyproject.toml replaces the "setup.py" package. When using 'flit' or 'poetry', only the pyproject.toml is required. +This project currently uses 'flit', but in the future may also include a 'poetry' example. Both are considered viable options. + +When using setuptools, and setup.cfg is still required. diff --git a/docs/vscode.md b/docs/vscode.md new file mode 100644 index 00000000..9987b82c --- /dev/null +++ b/docs/vscode.md @@ -0,0 +1 @@ +# Visual Studio Code for Python Development diff --git a/docs/workflows.md b/docs/workflows.md new file mode 100644 index 00000000..db885007 --- /dev/null +++ b/docs/workflows.md @@ -0,0 +1,4 @@ +# GitHub Workflow for Python + +The main workflow file is ".github/workflows/CI.yml". This performs linting, testing, and publishing for Python packages. +It can also be triggered manually on a specific branch. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..0e13834c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,295 @@ +[build-system] +requires = ["flit"] +build-backend = "flit.buildapi" + +[project] +name = "py-project-toml" +authors = [ + {name = "Daniel Ciborowski", email = "dciborow@microsoft.com"}, +] +description = "Sample Python Project for creating a new Python Module" +readme = "README.md" +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10" +] +requires-python = ">=3.7" +dynamic = ["version"] + +[project.optional-dependencies] +spark = [ + "pyspark>=3.0.0" +] +test = [ + "bandit[toml]==1.7.4", + "black==22.1.0", + "check-manifest==0.48", + "flake8-bugbear==22.1.11", + "flake8-docstrings", + "flake8-formatter_junit_xml", + "flake8==4.0.1", + "pre-commit==2.17.0", + "pylint==2.12.2", + "pylint_junit", + "pytest-cov==3.0.0", + "pytest-mock<3.7.1", + "pytest-runner", + "pytest==7.1.0", + "shellcheck-py==0.8.0.4" +] + +[project.urls] +Documentation = "https://github.com/microsoft/python-package-template/tree/main#readme" +Source = "https://github.com/microsoft/python-package-template" +Tracker = "https://github.com/microsoft/python-package-template/issues" + +[tool.flit.module] +name = "python_package" + +[tool.bandit] +exclude_dirs = ["build","dist","tests","scripts"] +number = 4 +recursive = true +targets = "src" + +[tool.black] +line-length = 120 +fast = true + +[tool.coverage.run] +branch = true + +[tool.coverage.report] +fail_under = 100 + +[tool.pyright] +include = ["src"] +exclude = [ + "**/node_modules", + "**/__pycache__", +] +venv = "env37" + +reportMissingImports = true +reportMissingTypeStubs = false + +pythonVersion = "3.7" +pythonPlatform = "Linux" + +executionEnvironments = [ + { root = "src" } +] + +[tool.pytest.ini_options] +addopts = "--cov-report xml:coverage.xml --cov src --cov-fail-under 0 --cov-append -m 'not integration'" +pythonpath = [ + "src" +] +testpaths = "tests" +junit_family = "xunit2" +markers = [ + "integration: marks as integration test", + "notebooks: marks as notebook test", + "gpu: marks as gpu test", + "spark: marks tests which need Spark", + "slow: marks tests as slow", + "unit: fast offline tests", +] + +[tool.tox] +legacy_tox_ini = """ +[tox] +envlist = py, integration, spark, all + +[testenv] +commands = + pytest -m "not integration and not spark" {posargs} + +[testenv:integration] +commands = + pytest -m "integration" {posargs} + +[testenv:spark] +extras = spark +setenv = + PYSPARK_DRIVER_PYTHON = {envpython} + PYSPARK_PYTHON = {envpython} +commands = + pytest -m "spark" {posargs} + +[testenv:all] +extras = all +setenv = + PYSPARK_DRIVER_PYTHON = {envpython} + PYSPARK_PYTHON = {envpython} +commands = + pytest {posargs} +""" + +[tool.pylint] +extension-pkg-whitelist= [ + "numpy", + "torch", + "cv2", + "pyodbc", + "pydantic", + "ciso8601", + "netcdf4", + "scipy" +] +ignore="CVS" +ignore-patterns="test.*?py,conftest.py" +init-hook='import sys; sys.setrecursionlimit(8 * sys.getrecursionlimit())' +jobs=0 +limit-inference-results=100 +persistent="yes" +suggestion-mode="yes" +unsafe-load-any-extension="no" + +[tool.pylint.'MESSAGES CONTROL'] +enable="c-extension-no-member" + +[tool.pylint.'REPORTS'] +evaluation="10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)" +output-format="text" +reports="no" +score="yes" + +[tool.pylint.'REFACTORING'] +max-nested-blocks=5 +never-returning-functions="sys.exit" + +[tool.pylint.'BASIC'] +argument-naming-style="snake_case" +attr-naming-style="snake_case" +bad-names= [ + "foo", + "bar" +] +class-attribute-naming-style="any" +class-naming-style="PascalCase" +const-naming-style="UPPER_CASE" +docstring-min-length=-1 +function-naming-style="snake_case" +good-names= [ + "i", + "j", + "k", + "ex", + "Run", + "_" +] +include-naming-hint="yes" +inlinevar-naming-style="any" +method-naming-style="snake_case" +module-naming-style="any" +no-docstring-rgx="^_" +property-classes="abc.abstractproperty" +variable-naming-style="snake_case" + +[tool.pylint.'FORMAT'] +ignore-long-lines="^\\s*(# )?.*['\"]??" +indent-after-paren=4 +indent-string=' ' +max-line-length=120 +max-module-lines=1000 +no-space-check= [ + "trailing-comma", + "dict-separator" +] +single-line-class-stmt="no" +single-line-if-stmt="no" + +[tool.pylint.'LOGGING'] +logging-format-style="old" +logging-modules="logging" + +[tool.pylint.'MISCELLANEOUS'] +notes= [ + "FIXME", + "XXX", + "TODO" +] + +[tool.pylint.'SIMILARITIES'] +ignore-comments="yes" +ignore-docstrings="yes" +ignore-imports="yes" +min-similarity-lines=7 + +[tool.pylint.'SPELLING'] +max-spelling-suggestions=4 +spelling-store-unknown-words="no" + +[tool.pylint.'STRING'] +check-str-concat-over-line-jumps="no" + +[tool.pylint.'TYPECHECK'] +contextmanager-decorators="contextlib.contextmanager" +generated-members="numpy.*,np.*,pyspark.sql.functions,collect_list" +ignore-mixin-members="yes" +ignore-none="yes" +ignore-on-opaque-inference="yes" +ignored-classes="optparse.Values,thread._local,_thread._local,numpy,torch,swagger_client" +ignored-modules="numpy,torch,swagger_client,netCDF4,scipy" +missing-member-hint="yes" +missing-member-hint-distance=1 +missing-member-max-choices=1 + +[tool.pylint.'VARIABLES'] +additional-builtins="dbutils" +allow-global-unused-variables="yes" +callbacks= [ + "cb_", + "_cb" +] +dummy-variables-rgx="_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_" +ignored-argument-names="_.*|^ignored_|^unused_" +init-import="no" +redefining-builtins-modules="six.moves,past.builtins,future.builtins,builtins,io" + +[tool.pylint.'CLASSES'] +defining-attr-methods= [ + "__init__", + "__new__", + "setUp", + "__post_init__" +] +exclude-protected= [ + "_asdict", + "_fields", + "_replace", + "_source", + "_make" +] +valid-classmethod-first-arg="cls" +valid-metaclass-classmethod-first-arg="cls" + +[tool.pylint.'DESIGN'] +max-args=5 +max-attributes=7 +max-bool-expr=5 +max-branches=12 +max-locals=15 +max-parents=7 +max-public-methods=20 +max-returns=6 +max-statements=50 +min-public-methods=2 + +[tool.pylint.'IMPORTS'] +allow-wildcard-with-all="no" +analyse-fallback-blocks="no" +deprecated-modules="optparse,tkinter.tix" + +[tool.pylint.'EXCEPTIONS'] +overgeneral-exceptions= [ + "BaseException", + "Exception" +] diff --git a/requirements.test.txt b/requirements.test.txt deleted file mode 100644 index 16a7c2bc..00000000 --- a/requirements.test.txt +++ /dev/null @@ -1,16 +0,0 @@ -bandit==1.7.4 -black==22.1.0 -check-manifest==0.48 -flake8-bugbear==22.1.11 -flake8-docstrings -flake8-formatter_junit_xml -flake8==4.0.1 -mypy==0.941 -pre-commit==2.17.0 -pylint==2.12.2 -pylint_junit -pytest-cov==3.0.0 -pytest-mock<3.7.1 -pytest-runner -pytest==7.1.0 -shellcheck-py==0.8.0.4 diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 8b137891..00000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/setup.py b/setup.py deleted file mode 100644 index 9200c29c..00000000 --- a/setup.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT license. - -import io -import os - -# NOTE: DO NOT change the import order, as sometimes there is a conflict between setuptools and distutils, -# it will cause following error: -# error: each element of 'ext_modules' option must be an Extension instance or 2-tuple -from setuptools import find_packages -from distutils.core import setup - -def read(file_path, encoding="utf-8"): - return io.open(file_path, encoding=encoding).read() - -readme = read("./README.md") - -setuptools = "setuptools>=54.2.0,<=54.2.0" - -core_requires = read("./requirements.txt") -test_requires = read("./requirements.test.txt") - -EXTRAS = { - "required": core_requires, - "test": test_requires, -} - -SETUP_REQUIRES = [ -] - - -setup( - name="ai-python-package", - version="0.0.1", - description="Microsoft Python Package Template", - long_description=readme, - long_description_content_type="text/x-rst", - author="Daniel Ciborowski", - author_email="dciborow@microsoft.com", - url="https://github.com/microsoft/ai-python", - project_urls={ - "Code": "https://github.com/microsoft/python-package", - "Issues": "https://github.com/microsoft/python-package/issues", - "Documents": "https://github.com/microsoft/python-package" - }, - license="MIT License", - platforms=["Windows", "Linux", "macOS"], - keywords=["template"], - classifiers=[ - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: MIT License', - 'Natural Language :: English', - 'Operating System :: MacOS', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX', - 'Operating System :: Unix', - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Topic :: Scientific/Engineering :: Artificial Intelligence"], - python_requires=">=3.7,<3.8", - setup_requires=SETUP_REQUIRES, - extras_require=EXTRAS, - packages=find_packages(exclude=["tests", "tests.*", "examples", "examples.*"]), - include_package_data=True, - zip_safe=False, -) diff --git a/src/python_package/__init__.py b/src/python_package/__init__.py new file mode 100644 index 00000000..91150271 --- /dev/null +++ b/src/python_package/__init__.py @@ -0,0 +1,8 @@ +# ------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See LICENSE in project root for information. +# ------------------------------------------------------------- +"""Python Package Template""" +from __future__ import annotations + +__version__ = "0.0.2" diff --git a/src/python_package/hello_world.py b/src/python_package/hello_world.py new file mode 100644 index 00000000..db43d690 --- /dev/null +++ b/src/python_package/hello_world.py @@ -0,0 +1,25 @@ +# --------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See LICENSE in project root for information. +# --------------------------------------------------------------------------------- +"""This is a Sample Python file.""" + + +from __future__ import annotations + + +def hello_world(i: int = 0) -> str: + """Doc String.""" + print("hello world") + return f"string-{i}" + + +def good_night() -> str: + """Doc String.""" + print("good night") + return "string" + + +def hello_goodbye(): + hello_world(1) + good_night() diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..ce475219 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,30 @@ +# --------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See LICENSE in project root for information. +# --------------------------------------------------------------------------------- +""" +This is a configuration file for pytest containing customizations and fixtures. + +In VSCode, Code Coverage is recorded in config.xml. Delete this file to reset reporting. +""" + +from __future__ import annotations + +from typing import List + +import pytest +from _pytest.nodes import Item + + +def pytest_collection_modifyitems(items: list[Item]): + for item in items: + if "spark" in item.nodeid: + item.add_marker(pytest.mark.spark) + elif "_int_" in item.nodeid: + item.add_marker(pytest.mark.integration) + + +@pytest.fixture +def unit_test_mocks(monkeypatch: None): + """Include Mocks here to execute all commands offline and fast.""" + pass diff --git a/tests/test_methods.py b/tests/test_methods.py index 42b78f26..40b57916 100644 --- a/tests/test_methods.py +++ b/tests/test_methods.py @@ -1,19 +1,32 @@ -import pytest +# --------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See LICENSE in project root for information. +# --------------------------------------------------------------------------------- +"""This is a sample python file for testing functions from the source code.""" +from __future__ import annotations + +from python_package.hello_world import hello_world -from ai-python-package.hello_world import hello_world def hello_test(): - hello_world() - -@pytest.fixture -def unit_test_mocks(monkeypatch): - """Include Mocks here to execute all commands offline and fast.""" - pass + """ + This defines the expected usage, which can then be used in various test cases. + Pytest will not execute this code directly, since the function does not contain the suffex "test" + """ + hello_world() + + +def test_hello(unit_test_mocks: None): + """ + This is a simple test, which can use a mock to override online functionality. + unit_test_mocks: Fixture located in conftest.py, implictly imported via pytest. + """ + hello_test() -@pytest.mark.offline -def test_hello(unit_test_mocks): - hello_test() -@pytest.mark.online -def test_hello_online(): - hello_test() +def test_int_hello(): + """ + This test is marked implicitly as an integration test because the name contains "_init_" + https://docs.pytest.org/en/6.2.x/example/markers.html#automatically-adding-markers-based-on-test-names + """ + hello_test() diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 16ded764..00000000 --- a/tox.ini +++ /dev/null @@ -1,102 +0,0 @@ -[tox] -# py will use whatever the basepython `python` maps to from PATH -# you can use py38, for example, to chosse a different version -# See https://tox.readthedocs.io/en/latest/config.html#tox-environments -envlist = py, cpu, gpu, spark, all - - -# Default env settings -[testenv] -# similar to 'pip install -*.whl[all]' -extras = all -commands = - # {posargs} will be substituded by arguments after the `--` when running. - # This will allow running subset of the test suite via tox. - # - # EX: tox -- -m "not spark and not gpu" - # will pass {-m "not spark and not gpu"} to `pytest` - # See https://tox.readthedocs.io/en/latest/example/general.html for more details - pytest {posargs} - -[testenv:cpu] -# i.e: 'pip install -*.whl[dev,examples]' -# with this dependency subset, we should be able to run the test markers: -# 1. "not notebooks and not spark and not gpu" (tests for general sdk utilities) -# 2. "notebooks and not spark and not gpu" (tests for notebook example without extra dependencies) -extras = dev,examples - -[testenv:gpu] -# i.e: 'pip install -*.whl[dev,gpu,examples]' -# with this dependency subset, we should be able to run the test markers: -# 1. "gpu and not notebook and not spark" (tests for gpu utilities) -# 2. "gpu and notebooks and not spark" (tests for notebooks needing gpu resources) -extras = dev,gpu,examples - -[testenv:spark] -# i.e: 'pip install -*.whl[dev,spark,examples]' -# with this dependency subset, we should be able to run the test markers: -# 1. "spark and not notebook and not spark" (test for spark utilities) -# 2. "spark and notebooks and not spark" (tests for notebook using spark) -extras = dev,spark,examples -# We will need to redefine the following envrionment var in tox -setenv = - PYSPARK_DRIVER_PYTHON = {envpython} - PYSPARK_PYTHON = {envpython} - -[testenv:all] -# i.e: 'pip install -*.whl[all]' -# with this, we should be able to run ANY tests -extras = all - -[testenv:flake8] -deps = flake8 -skip_install = True -commands = flake8 . - - -# Configurations for running pytest -[pytest] -log_cli = False -log_format = %(asctime)s %(levelname)s %(message)s -junit_family = xunit2 -# This enable custom marker as decorator "@pytest.mark.gpu" -markers = - # markers allow to us to run faster subset of the test: - # EX: pytest -m "not spark and not gpu" - # See https://docs.pytest.org/en/stable/example/markers.html#registering-markers - notebooks: mark a test as notebooks test - gpu: mark a test as gpu test - spark: mark a test as spark test -testpaths = - tests -addopts = - # reports all (except passed tests). See https://docs.pytest.org/en/latest/usage.html#detailed-summary-report - # TODO: please don't forget to replace with your python package name - -ra - --durations 10 - --cov-append --cov= --cov-report=term --cov-report=xml --junitxml=junit/test-results.xml - - -[coverage:report] -skip_empty = true -skip_covered = true - - -[flake8] -ignore = - # Lint rules to ignore - # W503 # line break before binary operator - # E203 # whitespace before ':' - -# File-specific flake8 ignore rules -per-file-ignores = - # F403 'from X import *' - # F405 'X' may be undefined, or defined from star imports - # src/path/to/file/*.py:F403,F405 - -max-line-length = 180 -exclude = - build, dist, docs, examples, - tests - .env*,.venv* # local virtual environments - .tox diff --git a/ws.code-workspace b/ws.code-workspace index 990a14b7..c3caffa3 100644 --- a/ws.code-workspace +++ b/ws.code-workspace @@ -5,38 +5,34 @@ } ], "settings": { - "files.trimTrailingWhitespace": true, - "files.autoSave": "onFocusChange", - "python.linting.lintOnSave": true, - "editor.trimAutoWhitespace": true, - "editor.formatOnSave": true, - "editor.formatOnPaste": true, - "python.analysis.typeCheckingMode": true, - "python.linting.enabled": true, + "files.trimTrailingWhitespace": true, + "files.autoSave": "onFocusChange", + "python.linting.lintOnSave": true, + "editor.trimAutoWhitespace": true, + "editor.formatOnSave": true, + "editor.formatOnPaste": true, + "python.analysis.typeCheckingMode": "strict", + "python.linting.enabled": true, "python.formatting.provider": "black", "python.formatting.blackArgs": [ "--config", - "black.toml" + "pyproject.toml" ], "python.linting.banditEnabled": true, "python.linting.banditArgs": [ "--configfile", - "tox.ini" + "pyproject.toml" ], "python.linting.flake8Enabled": true, "python.linting.flake8Args": [ "--config", - "tox.ini" + ".flake8" ], "python.linting.mypyEnabled": true, "python.linting.mypyArgs": [ "--config-file", - "tox.ini" + ".pyproject.toml" ], - "python.linting.pylintEnabled": true, - "python.linting.pylintArgs": [ - "--rcfile", - ".pylintrc" - ] + "python.linting.pylintEnabled": true } }