From d1d5e7052bf7be07457e2fa860a9fef65fbc5285 Mon Sep 17 00:00:00 2001 From: kklein Date: Mon, 5 Feb 2024 13:33:46 +0100 Subject: [PATCH] Initial commit. --- .gitattributes | 6 + .github/CODEOWNERS | 1 + .github/PULL_REQUEST_TEMPLATE.md | 8 + .github/assets/.condarc | 8 + .github/dependabot.yml | 20 ++ .github/workflows/ci.yml | 60 +++++ .github/workflows/package.yml | 21 ++ .gitignore | 401 +++++++++++++++++++++++++++++++ .pre-commit-config.yaml | 44 ++++ .prettierignore | 6 + .prettierrc | 7 + CHANGELOG.rst | 13 + conda.recipe/recipe.yaml | 37 +++ docs/Makefile | 20 ++ docs/changelog.rst | 1 + docs/conf.py | 111 +++++++++ docs/index.rst | 17 ++ docs/make.bat | 35 +++ environment.yml | 22 ++ metalearners/__init__.py | 9 + pyproject.toml | 76 ++++++ tests/test_core.py | 10 + 22 files changed, 933 insertions(+) create mode 100644 .gitattributes create mode 100644 .github/CODEOWNERS create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/assets/.condarc create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/package.yml create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 CHANGELOG.rst create mode 100644 conda.recipe/recipe.yaml create mode 100644 docs/Makefile create mode 120000 docs/changelog.rst create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 docs/make.bat create mode 100644 environment.yml create mode 100644 metalearners/__init__.py create mode 100644 pyproject.toml create mode 100644 tests/test_core.py diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8678f7e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +* text=auto + +*.{diff,patch} binary + +*.{py,yaml,yml,sh} text eol=lf +*.bat text eol=crlf diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..ebe3213 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @kklein diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..3593d7e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,8 @@ + + +# Checklist + +- [ ] Added a `CHANGELOG.rst` entry diff --git a/.github/assets/.condarc b/.github/assets/.condarc new file mode 100644 index 0000000..64eae06 --- /dev/null +++ b/.github/assets/.condarc @@ -0,0 +1,8 @@ +repodata_use_zst: true +channels: + - qc-internal + - conda-forge +custom_channels: + qc-internal: https://conda.prod.quantco.cloud/t/${QUETZ_API_KEY}/get/ +conda_build: + pkg_format: 2 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..7e49bbe --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,20 @@ +version: 2 +registries: + github: + type: git + url: https://github.com + username: x-access-token + password: ${{ secrets.DEPENDABOT_CONTENT_PAT }} +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly + reviewers: + - quantco/ci + registries: + - github + groups: + dependencies: + patterns: + - "*" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..0b706f0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,60 @@ +name: CI +on: [push] + +# Automatically stop old builds on the same branch/PR +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash -el {0} + +env: + QUETZ_API_KEY: ${{ secrets.QUETZ_API_KEY }} + +jobs: + pre-commit-checks: + name: Pre-commit checks - Python 3.10 + timeout-minutes: 30 + runs-on: ubuntu-latest + steps: + - name: Checkout branch + uses: actions/checkout@v4 + - name: Run pre-commit-conda + uses: quantco/pre-commit-conda@v1 + with: + python-version: "3.10" + + linux-unittests: + name: "Linux - unit tests - Python ${{ matrix.PYTHON_VERSION }}" + timeout-minutes: 30 + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + PYTHON_VERSION: ["3.8", "3.9", "3.10", "3.11"] + steps: + - name: Checkout branch + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + - name: Fetch full git history + run: git fetch --prune --unshallow + - name: Set up Conda env + uses: mamba-org/setup-micromamba@8767fb704bd78032e9392f0386bf46950bdd1194 + with: + condarc-file: .github/assets/.condarc + environment-file: environment.yml + cache-environment: true + create-args: >- + python=${{ matrix.PYTHON_VERSION }} + pytest-md + pytest-emoji + - name: Install repository + run: python -m pip install --no-build-isolation --no-deps --disable-pip-version-check -e . + - name: Run unittests + uses: quantco/pytest-action@v2 + with: + report-title: "Unit tests Linux - Python ${{ matrix.PYTHON_VERSION }}" + custom-arguments: --cov-report=xml ./tests diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml new file mode 100644 index 0000000..1edef06 --- /dev/null +++ b/.github/workflows/package.yml @@ -0,0 +1,21 @@ +name: Package +on: [push] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Build Package + runs-on: ubuntu-latest + steps: + - name: Checkout branch + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + - name: Build conda package + uses: quantco/rattler-build-action@v0.7.3 + with: + quetz-api-key: ${{ secrets.QUETZ_API_KEY }} + upload-quetz: ${{ startsWith(github.ref, 'refs/tags/') }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf2194a --- /dev/null +++ b/.gitignore @@ -0,0 +1,401 @@ +# Explicitly unignore hidden files for ripgrep. Individual folders can still +# be ignored in the blocks below. +!.* + +# NOTE: The block below is NOT copied sic from https://www.toptal.com/developers/gitignore. +# Instead, it contains some customizations that should be taken into account when adjusting the +# block. + +# Created by https://www.toptal.com/developers/gitignore/api/linux,macos,direnv,python,windows,pycharm+all,visualstudiocode,vim +# Edit at https://www.toptal.com/developers/gitignore?templates=linux,macos,direnv,python,windows,pycharm+all,visualstudiocode,vim + +### direnv ### +.direnv +.envrc + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### PyCharm+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PyCharm+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### Python ### +# 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 + +# pixi +# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +.pixi +#pixi.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/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +### VisualStudioCode ### +.vscode/ +#!.vscode/settings.json +#!.vscode/tasks.json +#!.vscode/launch.json +#!.vscode/extensions.json +#!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/linux,macos,direnv,python,windows,pycharm+all,visualstudiocode,vim + +# rattler-build +output/ +# setuptools-scm needs `.git/refs/tags`, don't ignore +!.git/refs/tags diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b5667e4 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,44 @@ +exclude: ^\.copier-answers\.yml$ +repos: + - repo: https://github.com/Quantco/pre-commit-mirrors-docformatter + rev: 1.7.5 + hooks: + - id: docformatter-conda + - repo: https://github.com/Quantco/pre-commit-mirrors-ruff + rev: 0.1.14 + hooks: + - id: ruff-conda + - repo: https://github.com/Quantco/pre-commit-mirrors-black + rev: 24.1.0 + hooks: + - id: black-conda + - repo: https://github.com/Quantco/pre-commit-mirrors-mypy + rev: 1.8.0 + hooks: + - id: mypy-conda + additional_dependencies: [-c, conda-forge, types-setuptools] + - repo: https://github.com/Quantco/pre-commit-mirrors-prettier + rev: 3.2.4 + hooks: + - id: prettier-conda + files: "\\.(md|yml|yaml)$" + - repo: https://github.com/Quantco/pre-commit-mirrors-pre-commit-hooks + rev: 4.5.0 + hooks: + - id: trailing-whitespace-conda + - id: end-of-file-fixer-conda + - id: check-merge-conflict-conda + args: ["--assume-in-merge"] + - repo: https://github.com/Quantco/pre-commit-mirrors-typos + rev: 1.17.2 + hooks: + - id: typos-conda + - repo: https://github.com/Quantco/pre-commit-insert-license + rev: "1.3.0" + hooks: + - id: insert-license + types: [python] + args: + - --dynamic-years + - --comment-style + - "#" diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..c835e2c --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +build +conda.recipe +.copier-answers.yml + +*.html +*.properties diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..478cc53 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "singleQuote": false, + "bracketSpacing": true, + "printWidth": 200, + "endOfLine": "auto", + "tabWidth": 2 +} diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 0000000..c7633c8 --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,13 @@ +.. Versioning follows semantic versioning, see also + https://semver.org/spec/v2.0.0.html. The most important bits are: + * Update the major if you break the public API + * Update the minor if you add new functionality + * Update the patch if you fixed a bug + +Changelog +========= + +1.0.0 (2023-MM-DD) +------------------ + +Initial release. diff --git a/conda.recipe/recipe.yaml b/conda.recipe/recipe.yaml new file mode 100644 index 0000000..489e0a8 --- /dev/null +++ b/conda.recipe/recipe.yaml @@ -0,0 +1,37 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/prefix-dev/recipe-format/main/schema.json + +context: + name: metalearners + version: ${{ env.get('GIT_DESCRIBE_TAG') | trim('v') }}${{ ('.post' + env.get('GIT_DESCRIBE_NUMBER') + '+' + env.get('GIT_DESCRIBE_HASH')) if env.get_default('GIT_DESCRIBE_NUMBER', '0') != '0' }} + +package: + name: ${{ name | lower }} + version: ${{ version }} + +source: + path: ../ + +build: + number: 0 + noarch: python + script: + - python -m pip install . --no-deps --ignore-installed -vv --no-build-isolation --disable-pip-version-check + +requirements: + host: + - python >=3.8 + - pip + - setuptools-scm + run: + - python >=3.8 + +tests: + - python: + imports: + - metalearners + pip_check: true + +about: + homepage: https://github.com/quantco/metalearners + license: LicenseRef-QuantCo + summary: "MetaLearners for CATE estimation" diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/changelog.rst b/docs/changelog.rst new file mode 120000 index 0000000..e22698b --- /dev/null +++ b/docs/changelog.rst @@ -0,0 +1 @@ +../CHANGELOG.rst \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..81ec193 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,111 @@ +# Copyright (c) QuantCo 2022-2024 +# SPDX-License-Identifier: LicenseRef-QuantCo + +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +import datetime +import importlib +import inspect +import os +import subprocess +import sys +from subprocess import CalledProcessError +from typing import cast + +_mod = importlib.import_module("metalearners") + + +project = "metalearners" +copyright = f"{datetime.date.today().year}, QuantCo, Inc" +author = "QuantCo, Inc." + +extensions = [ + "numpydoc", + "sphinx.ext.linkcode", + "sphinxcontrib.apidoc", +] + +apidoc_module_dir = "../metalearners" +apidoc_output_dir = "api" +apidoc_separate_modules = True +apidoc_extra_args = ["--implicit-namespaces"] + +templates_path = ["_templates"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] +html_theme = "sphinx_rtd_theme" +html_static_path = ["_static"] + + +# Copied and adapted from +# https://github.com/pandas-dev/pandas/blob/4a14d064187367cacab3ff4652a12a0e45d0711b/doc/source/conf.py#L613-L659 +# Required configuration function to use sphinx.ext.linkcode +def linkcode_resolve(domain, info): + """Determine the URL corresponding to a given Python object.""" + if domain != "py": + return None + + module_name = info["module"] + full_name = info["fullname"] + + _submodule = sys.modules.get(module_name) + if _submodule is None: + return None + + _object = _submodule + for _part in full_name.split("."): + try: + _object = getattr(_object, _part) + except AttributeError: + return None + + try: + fn = inspect.getsourcefile(inspect.unwrap(_object)) # type: ignore + except TypeError: + fn = None + if not fn: + return None + + try: + source, line_number = inspect.getsourcelines(_object) + except OSError: + line_number = None # type: ignore + + if line_number: + linespec = f"#L{line_number}-L{line_number + len(source) - 1}" + else: + linespec = "" + + fn = os.path.relpath(fn, start=os.path.dirname(cast(str, _mod.__file__))) + + try: + # See https://stackoverflow.com/a/21901260 + commit = ( + subprocess.check_output(["git", "rev-parse", "HEAD"]) + .decode("ascii") + .strip() + ) + except CalledProcessError: + # If subprocess returns non-zero exit status + commit = "main" + + return ( + "https://github.com/quantco/metalearners" + f"/blob/{commit}/{_mod.__name__.replace('.', '/')}/{fn}{linespec}" + ) diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..8b9f990 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,17 @@ +Welcome to metalearners's documentation! +======================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + API Reference + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..922152e --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..52bed94 --- /dev/null +++ b/environment.yml @@ -0,0 +1,22 @@ +name: metalearners +channels: + - qc-internal + - conda-forge + - nodefaults +dependencies: + # Git + - pre-commit + # Python + - pip + - python>=3.8 + - setuptools-scm + - setuptools>=61 # Adds support for pyproject.toml package declaration. + # Documentation + - make + - numpydoc + - sphinx + - sphinxcontrib-apidoc + - sphinx_rtd_theme + # Testing + - pytest>=6 # Adds --import-mode option + - pytest-cov diff --git a/metalearners/__init__.py b/metalearners/__init__.py new file mode 100644 index 0000000..3c3e0ed --- /dev/null +++ b/metalearners/__init__.py @@ -0,0 +1,9 @@ +# Copyright (c) QuantCo 2022-2024 +# SPDX-License-Identifier: LicenseRef-QuantCo + +import importlib.metadata + +try: + __version__ = importlib.metadata.version(__name__) +except Exception: + __version__ = "unknown" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..03a007a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,76 @@ +[build-system] +requires = ["setuptools", "setuptools-scm", "wheel"] + +[tool.setuptools_scm] +version_scheme = "post-release" + +[project] +name = "metalearners" +description = "MetaLearners for CATE estimation" +readme = "README.md" +dynamic = ["version"] +authors = [ + {name = "QuantCo, Inc.", email = "noreply@quantco.com"}, +] +classifiers = [ + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", +] +requires-python = ">=3.8" + +[project.urls] +repository = "https://github.com/quantco/metalearners" + + +[tool.setuptools.packages.find] +include = ["metalearners"] +namespaces = false + +[project.scripts] + +[tool.docformatter] +black = true + +[tool.black] +exclude = ''' +/( + \.eggs + | \.git + | \.venv + | build + | dist +)/ +''' + +[tool.ruff] +ignore = [ + "N803", # https://docs.astral.sh/ruff/rules/invalid-argument-name + "N806", # https://docs.astral.sh/ruff/rules/non-lowercase-variable-in-function + "E501", # https://docs.astral.sh/ruff/faq/#is-the-ruff-linter-compatible-with-black +] +line-length = 88 +select = [ + # pyflakes + "F", + # pycodestyle + "E", "W", + # isort + "I", + # pep8-naming + "N", + # pyupgrade + "UP", +] + +[tool.mypy] +python_version = '3.8' +ignore_missing_imports = true +no_implicit_optional = true +check_untyped_defs = true + + +[tool.pytest.ini_options] +addopts = "--import-mode=importlib --cov=metalearners --cov-report term-missing --color=yes" diff --git a/tests/test_core.py b/tests/test_core.py new file mode 100644 index 0000000..2b62dd0 --- /dev/null +++ b/tests/test_core.py @@ -0,0 +1,10 @@ +# Copyright (c) QuantCo 2022-2024 +# SPDX-License-Identifier: LicenseRef-QuantCo + + +def test_hard(): + import metalearners # noqa + + # this is just a demo that pytest can produce good error messages just by + # parsing assert statements + assert {"a": 1, "b": 2} == {"a": 1, "b": 2}