diff --git a/.github/workflows/ci_base.yml b/.github/workflows/ci_base.yml new file mode 100644 index 0000000..a503aaa --- /dev/null +++ b/.github/workflows/ci_base.yml @@ -0,0 +1,20 @@ +--- +name: CI libs/base + +on: + pull_request: + paths: + - 'dev-requirements.txt' + - 'pip-requirements.txt' + - '.github/workflows/python_reusable.yml' + - '.github/workflows/ci_base.yml' + - 'libs/base/**' + workflow_dispatch: # Allows to trigger the workflow manually in GitHub UI + +jobs: + ci-libs-base: + uses: + ./.github/workflows/python_reusable.yml + with: + working-directory: libs/base + secrets: inherit diff --git a/.github/workflows/ci_fancy.yml b/.github/workflows/ci_fancy.yml new file mode 100644 index 0000000..b8cad3d --- /dev/null +++ b/.github/workflows/ci_fancy.yml @@ -0,0 +1,21 @@ +--- +name: CI libs/fancy + +on: + pull_request: + paths: + - 'dev-requirements.txt' + - 'pip-requirements.txt' + - '.github/workflows/python_reusable.yml' + - '.github/workflows/ci_fancy.yml' + - 'libs/base/**' # libs/fancy depends on libs/base + - 'libs/fancy/**' + workflow_dispatch: # Allows to trigger the workflow manually in GitHub UI + +jobs: + ci-libs-fancy: + uses: + ./.github/workflows/python_reusable.yml + with: + working-directory: libs/fancy + secrets: inherit diff --git a/.github/workflows/python_reusable.yml b/.github/workflows/python_reusable.yml new file mode 100644 index 0000000..975d6f1 --- /dev/null +++ b/.github/workflows/python_reusable.yml @@ -0,0 +1,64 @@ +--- +name: Reusable Python library CI + +on: + workflow_call: + inputs: + working-directory: + required: true + type: string + description: "From which folder this pipeline executes" + install-packages: + description: "Space seperated list of packages to install using apt-get." + default: "" + type: string + # To avoid being billed 360 minutes if a step does not terminate + # (we've seen the setup-python step below do so!) + ci-timeout: + description: "The timeout of the ci job. Default is 25min" + default: 25 + type: number + +jobs: + local-ci-py-template: + runs-on: ubuntu-22.04 + timeout-minutes: 30 + + defaults: + run: + working-directory: ${{ inputs.working-directory }} + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Python + uses: actions/setup-python@v4 + timeout-minutes: 5 + with: + python-version-file: ${{ github.workspace }}/.python-version + cache: "pip" + cache-dependency-path: | + dev-requirements.txt + pip-requirements.txt + ${{ inputs.working-directory }}/requirements.txt + + - name: Install extra packages + if: ${{ inputs.install-packages != ''}} + run: | + sudo apt-get install -y ${{ inputs.install-packages }} + + - name: Install Python dependencies + run: | + pip install -r "$(git rev-parse --show-toplevel)/pip-requirements.txt" + pip install -r "$(git rev-parse --show-toplevel)/dev-requirements.txt" -r requirements.txt + + - name: Typecheck + run: | + pyright . + + - name: Test + run: | + python3 -m pytest tests/ # Assume that tests are in folder "tests/" diff --git a/.github/workflows/top_level.yml b/.github/workflows/top_level.yml new file mode 100644 index 0000000..e92818d --- /dev/null +++ b/.github/workflows/top_level.yml @@ -0,0 +1,54 @@ +--- +name: Top-level CI +on: + workflow_dispatch: + pull_request: + push: + branches: main # Comment this line if you want to test the CI before opening a PR + +jobs: + ci-global: + runs-on: ubuntu-22.04 + timeout-minutes: 10 + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Python + uses: actions/setup-python@v4 + timeout-minutes: 5 + with: + python-version-file: .python-version + cache: 'pip' + cache-dependency-path: | + pip-requirements.txt + dev-requirements.txt + + - name: Install Python dependencies + run: | + pip install -r pip-requirements.txt + pip install -r dev-requirements.txt + + # in order to test the template as well, we create a temporary library in the CI + - name: Create library with template + run: | + cookiecutter --no-input templates/pylibrary --output-dir libs/ + git add libs/library_name # So that checks below apply to generated library + + - name: Format Python imports + run: | + isort --check-only $(git ls-files "*.py") + + - name: Format Python + run: | + black --check $(git ls-files "*.py") + + - name: Lint Python + run: | + flake8 $(git ls-files "*.py") + + # Are all public symbols documented? (see top-level pyproject.toml configuration) + - name: Lint Python doc + run: | + pylint $(git ls-files "*.py") diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1da5487 --- /dev/null +++ b/.gitignore @@ -0,0 +1,164 @@ +################################################################## +# https://github.com/github/gitignore/blob/main/Python.gitignore # +################################################################## + +# 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/ diff --git a/.mergify.yml b/.mergify.yml new file mode 100644 index 0000000..58c1672 --- /dev/null +++ b/.mergify.yml @@ -0,0 +1,28 @@ +queue_rules: + - name: default + +pull_request_rules: + - name: automatic merge + conditions: + - "#approved-reviews-by>=1" + - "label=merge-queue" + - "base=main" + actions: + queue: + name: default + method: rebase + + - name: delete head branch after merge + conditions: + - merged + - closed + actions: + delete_head_branch: {} + + - name: remove from merge-queue after merge + conditions: + - merged + actions: + label: + remove: + - "merge-queue" diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..78c9a28 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.9.12 diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..efc3a71 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,58 @@ +# CODEOWNERS + +# This file declares which users are qualified to review and approve changes to +# specific files or paths in this repository. It helps contributors to identify +# whom to consult for assistance on merge requests. + +# To understand the syntax and semantics of this file, see: +# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners +# +# Note that rules defined later in the file take precedence over the rules +# defined earlier. + +# The owners in this rule will be the default owners for everything in +# the repo (if no other rule below matches the path of a modified file) +* @ribeirojose @mfw + +# Global files. Review load should be spread among engineers, +# to avoid burying one person under review duties +.gitignore @ribeirojose @mfw +.mergify.yml @ribeirojose @mfw + +# Python tooling. In a real setup, we recommend to create a GitHub team +# (https://docs.github.com/en/organizations/organizing-members-into-teams/about-teams) +# in charge of Python tooling. So the owner would be @your_org/python_team +.python-version @ribeirojose @mfw +dev-requirements.txt @ribeirojose @mfw +pip-requirements.txt @ribeirojose @mfw +pyproject.toml @ribeirojose @mfw +tox.ini @ribeirojose @mfw + +# CI. In a real setup, we recommend to create a GitHub team +# (https://docs.github.com/en/organizations/organizing-members-into-teams/about-teams) +# in charge of CI/CD. So the owner would be @your_org/cicd_team +# Note that the leading slash makes the rule include all nested subdirectories +/.github/workflows @ribeirojose @mfw + +# Libraries. The owner of a library is usually its first author, naturally +# spreading review load. +/libs/base @ribeirojose @mfw +/libs/fancy @ribeirojose @mfw + +# Scripts +/scripts @ribeirojose @mfw + +# Templates +/templates @ribeirojose @mfw + +# CODEOWNERS Owners +# +# These users are authorized to change authorizations. +# +# Note that this stanza MUST appear LAST so that it takes precedence over prior rules. +# This should be unnecessary if rules above are correct, but being safe. +# +# In a real setup, we recommend to create a GitHub team +# (https://docs.github.com/en/organizations/organizing-members-into-teams/about-teams) +# to group the administrators. So the owner would be @your_org/admins +/CODEOWNERS @ribeirojose @mfw diff --git a/README.md b/README.md index 0c3f06a..ad9aed6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,39 @@ # cow-py + CoW Protocol Python SDK [Help the herd 🐼](https://snapshot.org/#/cowgrants.eth/proposal/0x29bde0a0789a15f2255e11bdff088b4ffdf491729250dbe93b8b0776beb7f999) + + +## templated from python-monorepo-example + +This repository is a template for a Python monorepo with projects-specific +virtual environments. It uses [Pip](https://pypi.org/project/pip/) for installing dependencies. + +Because this repository is a [GitHub template](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository), +you can duplicate it by using +[_Creating a repository from a template_](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template) in the GitHub UI. It will create +a copy of this repository that is not a fork. + +This repository's design is explained on the Tweag blog: + +* [Python monorepo; part 1](https://www.tweag.io/blog/2023-04-04-python-monorepo-1/) + describes the monorepo's structure, how libraries are linked together and which + tools are used. +* [Python monorepo; part 2](https://www.tweag.io/blog/2023-07-13-python-monorepo-2) + describes the monorepo's CI, striking a good balance between being easy to use and being + featureful. + +The design strives to be simple, to work well in a startup environment where +CI specialists are not yet available, and yet to achieve a great deal +of [reproducibility](https://reproducible-builds.org/) to prepare for scaling. + +We use a virtual environment per library/project, to allow dependencies to +diverge if you need to. Another alternative is to have a single virtual environment +for the entire repository, to maximize uniformity. Choose what suits you best. + +We use [editable installs](https://setuptools.pypa.io/en/latest/userguide/development_mode.html) +for dependencies _within_ this repository, so that changes to a library are reflected +immediately in code depending on the said library. This implements +the _live at HEAD_ workflow, a term made popular by +[Titus Winters](https://www.youtube.com/watch?v=tISy7EJQPzI) from Google. diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 0000000..063464c --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1,7 @@ +black==23.1.0 +cookiecutter==2.1.1 +flake8==6.0.0 +isort==5.12.0 +pylint==2.16.3 +pyright==1.1.296 +pytest==7.2.1 diff --git a/libs/base/.tool-versions b/libs/base/.tool-versions new file mode 100644 index 0000000..926567c --- /dev/null +++ b/libs/base/.tool-versions @@ -0,0 +1 @@ +python 3.9.12 diff --git a/libs/base/README.md b/libs/base/README.md new file mode 100644 index 0000000..c7e4193 --- /dev/null +++ b/libs/base/README.md @@ -0,0 +1,42 @@ +# base + +A Python package by cow_py. + +The project owner is [@smelc](https://github.com/smelc). + +## Development + +If not already in a virtual environement, create and use one. +Read about it in the Python documentation: [venv — Creation of virtual environments](https://docs.python.org/3/library/venv.html). + +``` +python3 -m venv .venv +source .venv/bin/activate +``` + +Install the pinned pip version: + +``` +pip install -r $(git rev-parse --show-toplevel)/pip-requirements.txt +``` + +Finally, install the dependencies: + +``` +pip install -r $(git rev-parse --show-toplevel)/dev-requirements.txt -r requirements.txt +``` + +## Testing + +Execute tests from the library's folder (after having loaded the virtual environment, +see above) as follows: + +``` +python3 -m pytest tests/ +``` + +Execute the library's CI locally with [act](https://github.com/nektos/act) as follows: + +``` +act -j ci-libs-fancy +``` diff --git a/libs/base/cow_py/base/__init__.py b/libs/base/cow_py/base/__init__.py new file mode 100644 index 0000000..fabe685 --- /dev/null +++ b/libs/base/cow_py/base/__init__.py @@ -0,0 +1,3 @@ +from cow_py.base import adder2 + +__all__ = ["adder2"] diff --git a/libs/base/cow_py/base/adder2.py b/libs/base/cow_py/base/adder2.py new file mode 100644 index 0000000..01a277f --- /dev/null +++ b/libs/base/cow_py/base/adder2.py @@ -0,0 +1,14 @@ +"""Example module of the base library""" + + +def add2(x: int, y: int) -> int: + """ + Adds two integers + + Args: + x: the left operand + y: the right operand + Returns: + The sum of x and y + """ + return x + y diff --git a/libs/base/pyproject.toml b/libs/base/pyproject.toml new file mode 100644 index 0000000..67d34cc --- /dev/null +++ b/libs/base/pyproject.toml @@ -0,0 +1,36 @@ +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] +name = "cow_py-base" +version = "0.0.1" +description = "Shared library" +authors = [ "ClĂ©ment Hurlin " ] +packages = [ + { include = "cow_py" } +] + +[tool.poetry.dependencies] +python = ">=3.9" +numpy = ">=1.21" + +[tool.poetry.dev-dependencies] +black = "^23.1.0" +flake8 = "^6.0.0" +isort = "^5.12.0" +pylint = "^2.16.3" +pyright = "^1.1.296" +pytest = "^7.2.1" + +[tool.black] +line-length = 100 +target-version = ['py39'] + +[tool.pyright] + +[tool.pylint."messages control"] +disable = "all" +enable = ["empty-docstring", "missing-module-docstring", "missing-class-docstring", "missing-function-docstring"] +ignore = ["setup.py", "__init__.py"] +ignore-paths = ["tests"] diff --git a/libs/base/requirements.txt b/libs/base/requirements.txt new file mode 100644 index 0000000..cfe20ba --- /dev/null +++ b/libs/base/requirements.txt @@ -0,0 +1,4 @@ +# Add pinned dependencies to install for development +# Development dependencies (black, flake8, etc.) are in the top-level dev-requirements.txt file + +numpy==1.22.0 # Actually unused, for demonstration only diff --git a/libs/base/setup.cfg b/libs/base/setup.cfg new file mode 100644 index 0000000..1192d74 --- /dev/null +++ b/libs/base/setup.cfg @@ -0,0 +1 @@ +# This file is empty, but is needed by setuptools to detect pyproject.toml (for now) diff --git a/libs/base/tests/conftest.py b/libs/base/tests/conftest.py new file mode 100644 index 0000000..11afce2 --- /dev/null +++ b/libs/base/tests/conftest.py @@ -0,0 +1,13 @@ +""" +Shared configuration and fixtures for pytest. +""" + +import pytest + + +@pytest.fixture +def some_text() -> str: + """ + Some fixture which can be reused in all test modules. + """ + return "Some text" diff --git a/libs/base/tests/test_base.py b/libs/base/tests/test_base.py new file mode 100644 index 0000000..9d2c3a7 --- /dev/null +++ b/libs/base/tests/test_base.py @@ -0,0 +1,24 @@ +""" +Tests of adder2 +""" + +from cow_py import base + + +def test_zero() -> None: + """Tests the neutral element""" + assert base.adder2.add2(0, 0) == 0 + assert base.adder2.add2(0, 3) == 3 + assert base.adder2.add2(0, 3) == base.adder2.add2(3, 0) + + +def test_some() -> None: + """Some unit tests of add2""" + assert base.adder2.add2(1, 3) == 4 + assert base.adder2.add2(1, 3) == base.adder2.add2(3, 1) + assert base.adder2.add2(100, 2) == (base.adder2.add2(100, 1) + base.adder2.add2(1, 0)) + + +# In a true repository we would add property tests here, +# using hypothesis: +# https://hypothesis.readthedocs.io/en/latest/ diff --git a/libs/fancy/README.md b/libs/fancy/README.md new file mode 100644 index 0000000..b3a7626 --- /dev/null +++ b/libs/fancy/README.md @@ -0,0 +1,42 @@ +# fancy + +A Python package by cow_py. + +The project owner is our gentleman [@GuillaumeDesforges](https://github.com/GuillaumeDesforges). + +## Development + +If not already in a virtual environement, create and use one. +Read about it in the Python documentation: [venv — Creation of virtual environments](https://docs.python.org/3/library/venv.html). + +``` +python3 -m venv .venv +source .venv/bin/activate +``` + +Install the pinned pip version: + +``` +pip install -r $(git rev-parse --show-toplevel)/pip-requirements.txt +``` + +Finally, install the dependencies: + +``` +pip install -r $(git rev-parse --show-toplevel)/dev-requirements.txt -r requirements.txt +``` + +## Testing + +Execute tests from the library's folder (after having loaded the virtual environment, +see above) as follows: + +``` +python3 -m pytest tests/ +``` + +Execute the library's CI locally with [act](https://github.com/nektos/act) as follows: + +``` +act -j ci-libs-fancy +``` diff --git a/libs/fancy/cow_py/fancy/__init__.py b/libs/fancy/cow_py/fancy/__init__.py new file mode 100644 index 0000000..853cb41 --- /dev/null +++ b/libs/fancy/cow_py/fancy/__init__.py @@ -0,0 +1,3 @@ +from cow_py.fancy import adder3 + +__all__ = ["adder3"] diff --git a/libs/fancy/cow_py/fancy/adder3.py b/libs/fancy/cow_py/fancy/adder3.py new file mode 100644 index 0000000..8bb9db2 --- /dev/null +++ b/libs/fancy/cow_py/fancy/adder3.py @@ -0,0 +1,17 @@ +"""Example module of the fancy library, consuming the base library""" + +from cow_py import base + + +def add3(x: int, y: int, z: int) -> int: + """ + Adds three integers + + Args: + x: the left operand + y: the middle operand + z: the right operand + Returns: + The sum of x, y, and z + """ + return base.adder2.add2(base.adder2.add2(x, y), z) diff --git a/libs/fancy/pyproject.toml b/libs/fancy/pyproject.toml new file mode 100644 index 0000000..604fe8c --- /dev/null +++ b/libs/fancy/pyproject.toml @@ -0,0 +1,37 @@ +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] +name = "cow_py-fancy" +version = "0.0.1" +description = "High-level library" +authors = [ "ClĂ©ment Hurlin " ] +packages = [ + { include = "cow_py" } +] + +[tool.poetry.dependencies] +python = ">=3.9" +numpy = ">=1.21" +cow_py-base = "*" + +[tool.poetry.dev-dependencies] +black = "^23.1.0" +flake8 = "^6.0.0" +isort = "^5.12.0" +pylint = "^2.16.3" +pyright = "^1.1.296" +pytest = "^7.2.1" + +[tool.black] +line-length = 100 +target-version = ['py39'] + +[tool.pyright] + +[tool.pylint."messages control"] +disable = "all" +enable = ["empty-docstring", "missing-module-docstring", "missing-class-docstring", "missing-function-docstring"] +ignore = ["setup.py", "__init__.py"] +ignore-paths = ["tests"] diff --git a/libs/fancy/requirements.txt b/libs/fancy/requirements.txt new file mode 100644 index 0000000..1453fe8 --- /dev/null +++ b/libs/fancy/requirements.txt @@ -0,0 +1,4 @@ +# Add pinned dependencies to install for development +# Development dependencies (black, flake8, etc.) are in the top-level dev-requirements.txt file + +-e ../base diff --git a/libs/fancy/setup.cfg b/libs/fancy/setup.cfg new file mode 100644 index 0000000..1192d74 --- /dev/null +++ b/libs/fancy/setup.cfg @@ -0,0 +1 @@ +# This file is empty, but is needed by setuptools to detect pyproject.toml (for now) diff --git a/libs/fancy/tests/conftest.py b/libs/fancy/tests/conftest.py new file mode 100644 index 0000000..11afce2 --- /dev/null +++ b/libs/fancy/tests/conftest.py @@ -0,0 +1,13 @@ +""" +Shared configuration and fixtures for pytest. +""" + +import pytest + + +@pytest.fixture +def some_text() -> str: + """ + Some fixture which can be reused in all test modules. + """ + return "Some text" diff --git a/libs/fancy/tests/test_fancy.py b/libs/fancy/tests/test_fancy.py new file mode 100644 index 0000000..87367ef --- /dev/null +++ b/libs/fancy/tests/test_fancy.py @@ -0,0 +1,34 @@ +""" +Tests of adder3 +""" + +# from cow_py import base, fancy # don't! This makes pyright not able to link +# one of the two packages (is it https://github.com/microsoft/pyright/issues/2882?) +from cow_py import base +from cow_py import fancy + + +def test_zero() -> None: + """Test of add3 with zero""" + assert fancy.adder3.add3(0, 0, 0) == 0 + assert fancy.adder3.add3(0, 1, 0) == 1 + assert fancy.adder3.add3(0, 1, 2) == 3 + assert fancy.adder3.add3(1, 2, 0) == 3 + + +def test_add2_add3() -> None: + """Test relation between add2 and add3""" + assert fancy.adder3.add3(1, 2, 3) == (base.adder2.add2(1, 2) + base.adder2.add2(0, 3)) + assert fancy.adder3.add3(1, 2, 0) == base.adder2.add2(1, 2) + + +def test_some() -> None: + """Some unit tests of add3""" + assert fancy.adder3.add3(1, 2, 3) == 6 + assert fancy.adder3.add3(1, 2, 3) == fancy.adder3.add3(3, 2, 1) + assert fancy.adder3.add3(100, 2, 3) == 105 + + +# In a true repository we would add property tests here, +# using hypothesis: +# https://hypothesis.readthedocs.io/en/latest/ diff --git a/pip-requirements.txt b/pip-requirements.txt new file mode 100644 index 0000000..6c7e07f --- /dev/null +++ b/pip-requirements.txt @@ -0,0 +1,2 @@ +# We pin the version of pip, so that all commands use the same version +pip==24.0 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..595b6de --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,36 @@ +# Technically this section is not required, because this top-level pyproject.ml +# is only used for configuring tools, not for building anything. +# But we nevertheless have it, to be consistent with projects and libraries. +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] + +[tool.poetry.dev-dependencies] +black = "^23.1.0" +flake8 = "^6.0.0" +isort = "^5.12.0" +pyright = "^1.1.296" +pytest = "^7.2.1" + +[tool.black] +line-length = 100 +target-version = ['py39'] +force-exclude = ''' +^/archives/.*$ +''' + +[tool.pylint."messages control"] +disable = "all" +enable = ["empty-docstring", "missing-module-docstring", "missing-class-docstring", "missing-function-docstring"] +ignore = ["setup.py", "__init__.py"] +ignore-paths = ['.*/tests', ] + +[tool.isort] +profile = "black" +line_length = 100 +auto_identify_namespace_packages = false +force_single_line = true # pyright doesn't like implicit namespace + single line (related to https://github.com/microsoft/pyright/issues/2882?) +known_first_party = ["cow_py"] +extend_skip = ["archives"] diff --git a/scripts/rebuild-sandbox.sh b/scripts/rebuild-sandbox.sh new file mode 100755 index 0000000..ea079c7 --- /dev/null +++ b/scripts/rebuild-sandbox.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# +# Builds (or rebuilds) the sandbox of a library. Run as: +# +# ./$(git rev-parse --show-toplevel)/scripts/rebuilds-sandbox.sh +# +# in any library directory. You likely want to source the sandbox in your +# shell after having called this script. Do it with: +# +# source .venv/bin/activate + +set -x + +rm -Rf ".venv" +python3 -m venv .venv +source .venv/bin/activate +pip install -r $(git rev-parse --show-toplevel)/pip-requirements.txt +pip install -r $(git rev-parse --show-toplevel)/dev-requirements.txt -r requirements.txt diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..bf6ea39 --- /dev/null +++ b/tox.ini @@ -0,0 +1,9 @@ +# flake8 doesn't support configuration through pyproject.toml, hence this file +[flake8] +max-line-length = 100 +# required for compatibility with black +extend-ignore = N812, E203 +# list names frequently used for sandboxes +exclude = + .venv + venv