From d8a1ac67430550cfe645a3fd608c20ebca869ced Mon Sep 17 00:00:00 2001 From: Alex Wolf Date: Thu, 2 Jan 2025 12:50:32 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=89=20Initial=20commit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 83 ++++++++++++ .github/workflows/doc-changes.yml | 23 ++++ .gitignore | 108 ++++++++++++++++ .pre-commit-config.yaml | 51 ++++++++ LICENSE | 201 ++++++++++++++++++++++++++++++ README.md | 5 + docs/changelog.md | 5 + docs/guide.md | 7 ++ docs/index.md | 13 ++ docs/quickstart.ipynb | 73 +++++++++++ docs/reference.md | 5 + moscan/__init__.py | 18 +++ moscan/_core.py | 26 ++++ moscan/migrations/__init__.py | 0 moscan/models.py | 0 noxfile.py | 25 ++++ pyproject.toml | 139 +++++++++++++++++++++ tests/test_base.py | 7 ++ tests/test_notebooks.py | 9 ++ 19 files changed, 798 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/doc-changes.yml create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 docs/changelog.md create mode 100644 docs/guide.md create mode 100644 docs/index.md create mode 100644 docs/quickstart.ipynb create mode 100644 docs/reference.md create mode 100644 moscan/__init__.py create mode 100644 moscan/_core.py create mode 100644 moscan/migrations/__init__.py create mode 100644 moscan/models.py create mode 100644 noxfile.py create mode 100644 pyproject.toml create mode 100644 tests/test_base.py create mode 100644 tests/test_notebooks.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..eacbb01 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,83 @@ +name: build + +on: + push: + branches: [main] + pull_request: + branches: [main, staging] + +jobs: + build: + runs-on: ubuntu-latest + env: + GITHUB_EVENT_NAME: ${{ github.event_name }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + python: "3.12" + - os: ubuntu-latest + python: "3.12" + pip-flags: "--pre" + group: ["unit", "docs"] + timeout-minutes: 15 + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/checkout@v4 + if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} + with: + repository: laminlabs/lndocs + ssh-key: ${{ secrets.READ_LNDOCS }} + path: lndocs + ref: main + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + + - uses: actions/cache@v4 + with: + path: ~/.cache/pre-commit + key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }} + - run: pip install git+https://github.com/laminlabs/laminci + # - run: sudo apt-get -y install graphviz + + - uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + + - name: Run lint + if: matrix.group == 'unit' + run: nox -s lint + + - name: Run build + if: ${{ !(matrix.group == 'docs' && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }} + run: nox -s "build(group='${{ matrix.group }}')" + + - uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + - uses: cloudflare/wrangler-action@v3 + if: ${{ !(matrix.group == 'docs' && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }} + id: cloudflare + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: 472bdad691b4483dea759eadb37110bd + command: pages deploy "_build/html" --project-name=PROJECTNAME + gitHubToken: ${{ secrets.GITHUB_TOKEN }} + - uses: edumserrano/find-create-or-update-comment@v2 + if: ${{ !(matrix.group == 'docs' && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }} + with: + issue-number: ${{ github.event.pull_request.number }} + body-includes: "Deployment URL" + comment-author: "github-actions[bot]" + body: | + Deployment URL: ${{ steps.cloudflare.outputs.deployment-url }} + edit-mode: replace diff --git a/.github/workflows/doc-changes.yml b/.github/workflows/doc-changes.yml new file mode 100644 index 0000000..70dfcc7 --- /dev/null +++ b/.github/workflows/doc-changes.yml @@ -0,0 +1,23 @@ +name: doc-changes + +on: + pull_request_target: + branches: + - main + types: + - closed + +jobs: + latest-changes: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + - run: pip install "laminci[doc-changes]@git+https://x-access-token:${{ secrets.LAMIN_BUILD_DOCS }}@github.com/laminlabs/laminci" + - run: laminci doc-changes + env: + repo_token: ${{ secrets.GITHUB_TOKEN }} + docs_token: ${{ secrets.LAMIN_BUILD_DOCS }} + changelog_file: PATH_TO_CHANGELOG_FILE diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..11f2b44 --- /dev/null +++ b/.gitignore @@ -0,0 +1,108 @@ +# macOS +.DS_Store +.AppleDouble +.LSOverride + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# 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/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# dotenv +.env + +# virtualenv +.venv +venv/ +ENV/ + +# mypy +.mypy_cache/ + +# IDE settings +.vscode/ +.idea/ + +# Lamin +_build +docs/moscan.* +lamin_sphinx +docs/conf.py +_docs_tmp* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..ceaf80e --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,51 @@ +fail_fast: false +default_language_version: + python: python3 +default_stages: + - commit + - push +minimum_pre_commit_version: 2.12.0 +repos: + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v4.0.0-alpha.4 + hooks: + - id: prettier + exclude: | + (?x)( + docs/changelog.md + ) + - repo: https://github.com/kynan/nbstripout + rev: 0.6.1 + hooks: + - id: nbstripout + exclude: | + (?x)( + docs/examples/| + docs/notes/ + ) + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.7 + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix, --unsafe-fixes] + - id: ruff-format + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: detect-private-key + - id: check-ast + - id: end-of-file-fixer + exclude: | + (?x)( + .github/workflows/latest-changes.jinja2 + ) + - id: mixed-line-ending + args: [--fix=lf] + - id: trailing-whitespace + - id: check-case-conflict + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.7.1 + hooks: + - id: mypy + args: [--no-strict-optional, --ignore-missing-imports] + additional_dependencies: ["types-requests", "types-attrs"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b09cd78 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ef86977 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Moscan: Model-based single-cell analytics + +## Contributing + +Please run `pre-commit install` and `gitmoji -i` on the CLI before starting to work on this repository! diff --git a/docs/changelog.md b/docs/changelog.md new file mode 100644 index 0000000..35dec83 --- /dev/null +++ b/docs/changelog.md @@ -0,0 +1,5 @@ +# Changelog + + +Name | PR | Developer | Date | Version +--- | --- | --- | --- | --- diff --git a/docs/guide.md b/docs/guide.md new file mode 100644 index 0000000..f1fbf33 --- /dev/null +++ b/docs/guide.md @@ -0,0 +1,7 @@ +# Guide + +```{toctree} +:maxdepth: 1 + +quickstart +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..8463317 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,13 @@ +```{include} ../README.md +:start-line: 0 +:end-line: 1 +``` + +```{toctree} +:maxdepth: 1 +:hidden: + +guide +reference +changelog +``` diff --git a/docs/quickstart.ipynb b/docs/quickstart.ipynb new file mode 100644 index 0000000..0ad2a2b --- /dev/null +++ b/docs/quickstart.ipynb @@ -0,0 +1,73 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7f26a335-cf1c-4e69-be3b-0c26b154606a", + "metadata": {}, + "source": [ + "# Quickstart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a51faaf3-ee27-43c6-b48d-13be1adb5c46", + "metadata": {}, + "outputs": [], + "source": [ + "from moscan import ExampleClass, example_function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa758625-efb0-4047-9a03-aa534bf6f78e", + "metadata": {}, + "outputs": [], + "source": [ + "example_function(\"A\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ad22eec0-8f75-4e0b-a132-8a43ccb09b37", + "metadata": {}, + "outputs": [], + "source": [ + "ex = ExampleClass(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8bf01623-742e-425a-973d-132fc61c3d9c", + "metadata": {}, + "outputs": [], + "source": [ + "assert ex.bar() == \"hello\"" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/reference.md b/docs/reference.md new file mode 100644 index 0000000..9bfcaee --- /dev/null +++ b/docs/reference.md @@ -0,0 +1,5 @@ +# Reference + +```{eval-rst} +.. automodule:: moscan +``` diff --git a/moscan/__init__.py b/moscan/__init__.py new file mode 100644 index 0000000..002dfb8 --- /dev/null +++ b/moscan/__init__.py @@ -0,0 +1,18 @@ +"""Model-based single-cell analytics. + +Import the package:: + + import moscan + +This is the complete API reference: + +.. autosummary:: + :toctree: . + + example_function + ExampleClass +""" + +__version__ = "0.0.1" # denote a pre-release for 0.1.0 with 0.1rc1 + +from ._core import ExampleClass, example_function diff --git a/moscan/_core.py b/moscan/_core.py new file mode 100644 index 0000000..f6882ed --- /dev/null +++ b/moscan/_core.py @@ -0,0 +1,26 @@ +def example_function(column_name: str) -> str: + """Lower case your input string. + + Args: + column_name: Column name to transform to lower case. + + Returns: + The lower-cased column name. + """ + return column_name.lower() + + +class ExampleClass: + """Awesome class.""" + + def __init__(self, value: int): + print("initializing") + + def bar(self) -> str: + """Bar function.""" + return "hello" + + @property + def foo(self) -> str: + """Foo property.""" + return "hello" diff --git a/moscan/migrations/__init__.py b/moscan/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/moscan/models.py b/moscan/models.py new file mode 100644 index 0000000..e69de29 diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000..6d8b308 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,25 @@ +import nox +from laminci.nox import build_docs, login_testuser1, run_pre_commit, run_pytest + +# we'd like to aggregate coverage information across sessions +# and for this the code needs to be located in the same +# directory in every github action runner +# this also allows to break out an installation section +nox.options.default_venv_backend = "none" + + +@nox.session +def lint(session: nox.Session) -> None: + run_pre_commit(session) + + +@nox.session() +@nox.parametrize("group", ["unit", "docs"]) +def build(session, group): + session.run(*"uv pip install --system -e .[dev]".split()) + login_testuser1(session) + + if group == "unit": + run_pytest(session) + elif group == "docs": + build_docs(session, strict=True) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..378f59a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,139 @@ +[build-system] +requires = ["flit_core >=3.2,<4"] +build-backend = "flit_core.buildapi" + +[project] +name = "moscan" +requires-python = ">=3.9,<3.13" +authors = [{name = "Lamin Labs", email = "open-source@lamin.ai"}] +readme = "README.md" +dynamic = ["version", "description"] +dependencies = [ + "lamindb", +] + +[project.urls] +Home = "https://github.com/laminlabs/moscan" + +[project.optional-dependencies] +dev = [ + "pre-commit", + "nox", + "pytest>=6.0", + "pytest-cov", + "nbproject_test", +] + +[tool.pytest.ini_options] +testpaths = [ + "tests", +] +filterwarnings = [ + "ignore:Jupyter is migrating its paths to use standard platformdirs:DeprecationWarning" +] + +[tool.ruff] +src = ["src"] +line-length = 88 +select = [ + "F", # Errors detected by Pyflakes + "E", # Error detected by Pycodestyle + "W", # Warning detected by Pycodestyle + "I", # isort + "D", # pydocstyle + "B", # flake8-bugbear + "TID", # flake8-tidy-imports + "C4", # flake8-comprehensions + "BLE", # flake8-blind-except + "UP", # pyupgrade + "RUF100", # Report unused noqa directives + "TCH", # Typing imports + "NPY", # Numpy specific rules + "PTH", # Use pathlib + "S" # Security +] +ignore = [ + # Do not catch blind exception: `Exception` + "BLE001", + # Errors from function calls in argument defaults. These are fine when the result is immutable. + "B008", + # line too long -> we accept long comment lines; black gets rid of long code lines + "E501", + # Do not assign a lambda expression, use a def -> lambda expression assignments are convenient + "E731", + # allow I, O, l as variable names -> I is the identity matrix + "E741", + # Missing docstring in public module + "D100", + # undocumented-public-class + "D101", + # Missing docstring in public method + "D102", + # Missing docstring in public function + "D103", + # Missing docstring in public package + "D104", + # __magic__ methods are are often self-explanatory, allow missing docstrings + "D105", + # Missing docstring in public nested class + "D106", + # Missing docstring in __init__ + "D107", + ## Disable one in each pair of mutually incompatible rules + # We don’t want a blank line before a class docstring + "D203", + # 1 blank line required after class docstring + "D204", + # first line should end with a period [Bug: doesn't work with single-line docstrings] + # We want docstrings to start immediately after the opening triple quote + "D213", + # Section underline is over-indented ("{name}") + "D215", + # First line should end with a period + "D400", + # First line should be in imperative mood; try rephrasing + "D401", + # First word of the first line should be capitalized: {} -> {} + "D403", + # First word of the docstring should not be "This" + "D404", + # Section name should end with a newline ("{name}") + "D406", + # Missing dashed underline after section ("{name}") + "D407", + # Section underline should be in the line following the section's name ("{name}") + "D408", + # Section underline should match the length of its name ("{name}") + "D409", + # No blank lines allowed between a section header and its content ("{name}") + "D412", + # Missing blank line after last section ("{name}") + "D413", + # Imports unused + "F401", + # camcelcase imported as lowercase + "N813", + # module import not at top level of file + "E402", + # open()` should be replaced by `Path.open() + "PTH123", + # subprocess` call: check for execution of untrusted input - https://github.com/PyCQA/bandit/issues/333 + "S603", + # Starting a process with a partial executable path + "S607" +] + +[tool.ruff.pydocstyle] +convention = "google" + +[tool.ruff.per-file-ignores] +"docs/*" = ["I"] +"tests/**/*.py" = [ + "D", # docstrings are allowed to look a bit off + "S101", # asserts allowed in tests... + "ARG", # Unused function args -> fixtures nevertheless are functionally relevant... + "FBT", # Don't care about booleans as positional arguments in tests, e.g. via @pytest.mark.parametrize() + "PLR2004", # Magic value used in comparison, ... + "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes +] +"*/__init__.py" = ["F401"] diff --git a/tests/test_base.py b/tests/test_base.py new file mode 100644 index 0000000..16d371d --- /dev/null +++ b/tests/test_base.py @@ -0,0 +1,7 @@ +from moscan import ExampleClass, example_function + + +def test_dummy(): + assert example_function("A") == "a" + ex = ExampleClass(1) + assert ex.bar() == "hello" diff --git a/tests/test_notebooks.py b/tests/test_notebooks.py new file mode 100644 index 0000000..8ccfd84 --- /dev/null +++ b/tests/test_notebooks.py @@ -0,0 +1,9 @@ +from pathlib import Path + +import nbproject_test as test + + +def test_notebooks(): + docs_folder = Path(__file__).parents[1] / "docs/" + for check_folder in docs_folder.glob("./**"): + test.execute_notebooks(check_folder, write=True)