From d8e91341f069da51531180b63edcf26db3e764b1 Mon Sep 17 00:00:00 2001 From: ddelange <14880945+ddelange@users.noreply.github.com> Date: Mon, 26 Oct 2020 20:26:53 +0100 Subject: [PATCH] :tada: Initial commit --- .github/CODEOWNERS | 1 + .github/workflows/CD.yml | 36 ++++++++++++++ .github/workflows/CI.yml | 71 +++++++++++++++++++++++++++ .gitignore | 2 + .pre-commit-config.yaml | 68 ++++++++++++++++++++++++++ .readthedocs.yaml | 25 ++++++++++ MANIFEST.in | 8 +++ Makefile | 37 ++++++++++++++ README.md | 60 +++++++++++++++++++++++ docs/Makefile | 29 +++++++++++ docs/source/conf.py | 103 +++++++++++++++++++++++++++++++++++++++ docs/source/index.rst | 23 +++++++++ requirements/ci.txt | 9 ++++ requirements/docs.txt | 3 ++ requirements/prod.txt | 0 setup.cfg | 41 ++++++++++++++++ setup.py | 55 +++++++++++++++++++++ src/mapply/__init__.py | 2 + 18 files changed, 573 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/CD.yml create mode 100644 .github/workflows/CI.yml create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 .readthedocs.yaml create mode 100644 MANIFEST.in create mode 100644 Makefile create mode 100644 README.md create mode 100755 docs/Makefile create mode 100755 docs/source/conf.py create mode 100755 docs/source/index.rst create mode 100644 requirements/ci.txt create mode 100644 requirements/docs.txt create mode 100644 requirements/prod.txt create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 src/mapply/__init__.py diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..ff45e32 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @ddelange diff --git a/.github/workflows/CD.yml b/.github/workflows/CD.yml new file mode 100644 index 0000000..3982080 --- /dev/null +++ b/.github/workflows/CD.yml @@ -0,0 +1,36 @@ +# This workflows will upload a Python Package using Twine when a release is created +# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries + +name: CD + +on: + release: + types: [released] + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.x + + - name: Install dependencies + run: | + pip install -U pip setuptools wheel + pip install setuptools wheel twine + + - name: Build and publish + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + python setup.py sdist bdist_wheel --universal + twine upload dist/* diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..91d5141 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,71 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: CI +on: + pull_request: + push: + branches: master + +jobs: + autocancel: + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + steps: + - uses: n1hility/cancel-previous-runs@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [2.7, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9] + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Pip cache + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements/*.txt') }} + + - name: Pre-commit cache + uses: actions/cache@v2 + with: + path: ~/.cache/pre-commit + key: ${{ runner.os }}-pre-commit-${{ matrix.python-version }}-${{ hashFiles('**/requirements/ci.txt') }}-${{ hashFiles('.pre-commit-config.yaml') }} + + - name: Install (ci) + run: | + pip install --upgrade pip setuptools wheel + pip install -r requirements/ci.txt + pip install -r requirements/docs.txt + pip install codecov + + - name: Lint + if: matrix.python-version != 2.7 + run: make lint + + - name: Install (self) + run: pip install -e . + + - name: Test + run: make test + + - name: Docs + if: matrix.python-version != 2.7 + run: SPHINXOPTS=-W make builddocs + + - name: Codecov + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + run: codecov diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9d559eb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# setuptools_scm +_version.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..8be35f6 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,68 @@ +repos: +- repo: https://github.com/Yelp/detect-secrets + rev: v0.14.3 + hooks: + - id: detect-secrets + args: ['--baseline', '.secrets.baseline'] + exclude: .*/tests/.* + +- repo: https://github.com/psf/black + rev: 20.8b1 + hooks: + - id: black + +- repo: https://github.com/timothycrosley/isort + rev: 5.5.4 + hooks: + - id: isort + +- repo: local + hooks: + - id: mypy + name: Run mypy + entry: python -m mypy src/ + language: system + types: [python] + pass_filenames: false + +- repo: https://github.com/PyCQA/flake8 + rev: 3.8.3 + hooks: + - id: flake8 + additional_dependencies: [ + 'darglint~=1.5.4', + 'flake8-absolute-import~=1.0', + 'flake8-blind-except~=0.1.1', + 'flake8-builtins~=1.5.3', + 'flake8-cognitive-complexity==0.1.0', + 'flake8-comprehensions~=3.2.3', + 'flake8-docstrings~=1.5.0', + 'flake8-logging-format~=0.6.0', + 'flake8-mutable~=1.2.0', + 'flake8-print~=3.1.4', + 'flake8-printf-formatting~=1.1.0', + 'flake8-pytest-style~=1.2.3', + 'flake8-quotes~=3.2.0', + 'flake8-tuple~=0.4.1', + 'pep8-naming~=0.11.1' + ] + +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: mixed-line-ending + args: ['--fix=lf'] + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-merge-conflict + - id: check-json + - id: check-toml + - id: check-xml + - id: check-yaml + - id: debug-statements + +- repo: https://github.com/econchick/interrogate + rev: 1.3.1 + hooks: + - id: interrogate + pass_filenames: false diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..83de6f4 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,25 @@ +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/source/conf.py + +# Build documentation with MkDocs +#mkdocs: +# configuration: mkdocs.yml + +# Optionally build your docs in additional formats such as PDF and ePub +formats: all + +# Optionally set the version of Python and requirements required to build your docs +python: + version: 3.8 + install: + - method: pip + path: . + - requirements: requirements/docs.txt diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..408609d --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,8 @@ +global-exclude * +include setup.py +include README* +include LICENSE* +graft src +graft requirements +exclude requirements/ci.txt +recursive-exclude * __pycache__ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2541e8c --- /dev/null +++ b/Makefile @@ -0,0 +1,37 @@ +.PHONY: lint +## Run linting +lint: + pre-commit run --all-files + +.PHONY: test +## Run tests +test: + python -m pytest + +.PHONY: showcov +## Open the test coverage overview using the default HTML viewer +showcov: + xdg-open htmlcov/index.html || open htmlcov/index.html + +.PHONY: install +## Install this repo, plus dev requirements, in editable mode +install: + pip install -r requirements/ci.txt -r requirements/docs.txt + pip install -e . + pre-commit install + +.PHONY: builddocs +## Build documentation using Sphinx +builddocs: + cd docs && make docs + +.PHONY: showdocs +## Open the docs using the default HTML viewer +showdocs: + xdg-open docs/_build/html/index.html || open docs/_build/html/index.html + +.PHONY: help +## Print Makefile documentation +help: + @perl -0 -nle 'printf("%-25s - %s\n", "$$2", "$$1") while m/^##\s*([^\r\n]+)\n^([\w-]+):[^=]/gm' $(MAKEFILE_LIST) | sort +.DEFAULT_GOAL := help diff --git a/README.md b/README.md new file mode 100644 index 0000000..a2540da --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# mapply + +[![build](https://img.shields.io/github/workflow/status/ddelange/mapply/GH/master?logo=github&cacheSeconds=86400)](https://github.com/ddelange/mapply/actions?query=branch%3Amaster) +[![codecov](https://img.shields.io/codecov/c/github/ddelange/mapply/master?logo=codecov&logoColor=white)](https://codecov.io/gh/ddelange/mapply) +[![pypi Version](https://img.shields.io/pypi/v/mapply.svg?logo=pypi&logoColor=white)](https://pypi.org/project/mapply/) +[![python](https://img.shields.io/pypi/pyversions/mapply.svg?logo=python&logoColor=white)](https://pypi.org/project/mapply/) +[![downloads](https://pepy.tech/badge/mapply)](https://pypistats.org/packages/mapply) +[![black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/black) + + +## Initial setup of this repo + +Add this repo to Github: + +- [Create a new repository](https://github.com/new) on GitHub. Only fill in `mapply` and an optional description and click `Create repository`. Do not initialize the new repository with README, license, or gitignore files. + +- Now push this repo to Github (`__version__` is populated based on tags, so tag the initial commit): + +```sh +cd mapply +git init . +git add . +git commit -m ':tada: Initial commit' +git tag -a "0.1.0-rc.1" -m 'Initial release candidate. Bump version on GitHub and it will be reflected on the next `git pull; pip install -e .`' +git remote add origin https://github.com/ddelange/mapply.git +git push --set-upstream origin master +``` + +- This repo contains GitHub Actions to to run `linting`, `tests`, `codecov`, and `PyPi` deploys for all GitHub releases. + + - This requires `$PYPI_USER` and `$PYPI_PASSWORD` and `$CODECOV_TOKEN` (found under `Repository Upload Token` at https://codecov.io/gh/ddelange/mapply/settings) + + - Add these variables to the repo's secrets here: https://github.com/ddelange/mapply/settings/secrets + +- It is also recommended to make `master` a protected branch. The first two ticks should be enough (`Require branches to be up to date before merging` is also nice, and `Include administrators` will avoid accidental pushes to `master`): https://github.com/ddelange/mapply/settings/branch_protection_rules/new + +- If you'd like, add a LICENSE.md file manually or via GitHub GUI (don't forget to pull afterwards), and add an appropriate keyword to [`setup()`](setup.py), e.g. `license="MIT"`, and the appropriate [classifier](https://pypi.org/classifiers/), e.g. `"License :: OSI Approved :: MIT License"`. + +- You can remove this (now unnecessary) section. + +## Installation + +This pure-Python, OS independent package is available on [PyPI](https://pypi.org/project/mapply): + +```sh +$ pip install mapply +``` + +## Usage + +```py +# TODO +``` + +## Development + +[![gitmoji](https://img.shields.io/badge/gitmoji-%20%F0%9F%98%9C%20%F0%9F%98%8D-ffdd67)](https://github.com/carloscuesta/gitmoji-cli) +[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) + +Run `make help` for options like installing for development, linting, testing, and building docs. diff --git a/docs/Makefile b/docs/Makefile new file mode 100755 index 0000000..5cf6a41 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,29 @@ + +# 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 = source +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) + +show: + xdg-open docs/_build/html/index.html || open docs/_build/html/index.html + +docs: + rm -rf source/_code_reference + make clean + make html diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100755 index 0000000..586fe84 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,103 @@ +# 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 sys +from pathlib import Path + +from sphinx.ext import apidoc + +from mapply import __version__ + +current_dir = Path(__file__).parent.absolute() +base_dir = current_dir.parents[1] +code_dir = base_dir / "src" / "mapply" + +sys.path.insert(0, str(code_dir)) + +readme_dest = current_dir / "README.md" +readme_src = base_dir / "README.md" + +if readme_dest.exists(): + readme_dest.unlink() +readme_dest.symlink_to(readme_src) + +# -- Project information ----------------------------------------------------- + +project = "mapply" +author = "ddelange" +copyright = "ddelange" + +# The full version, including alpha/beta/rc tags +release = __version__ + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "recommonmark", + "sphinx_rtd_theme", + "sphinx.ext.autodoc", + "sphinx.ext.coverage", + "sphinx.ext.napoleon", +] +autodoc_typehints = "description" + +# recommonmark extension allows mixed filetypes +source_suffix = [".rst", ".md"] + +# Add any paths that contain templates here, relative to this directory. +# templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = "sphinx_rtd_theme" + + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# html_static_path = ["_static"] + + +def run_apidoc(_): + exclude = [] + + argv = [ + "--doc-project", + "Code Reference", + "-M", + "-f", + "-d", + "3", + "--tocfile", + "index", + "-o", + str(current_dir / "_code_reference"), + str(code_dir), + ] + exclude + + apidoc.main(argv) + + +def setup(app): + app.connect("builder-inited", run_apidoc) diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100755 index 0000000..685353b --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,23 @@ +.. awsstepfuncs documentation master file, created by + sphinx-quickstart on Thu Jul 2 09:53:53 2020. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to mapply's documentation! +=================================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + Introduction + _code_reference/index + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/requirements/ci.txt b/requirements/ci.txt new file mode 100644 index 0000000..92ef378 --- /dev/null +++ b/requirements/ci.txt @@ -0,0 +1,9 @@ +detect-secrets~=0.14.2 +mypy~=0.782 +pre-commit~=2.6.0 +pytest-cov~=2.10.1 +pytest-env~=0.6.2 +pytest-randomly~=3.4.1 +pytest-sugar~=0.9.4 +pytest~=4.6 +testfixtures~=6.14.1 diff --git a/requirements/docs.txt b/requirements/docs.txt new file mode 100644 index 0000000..4b49b8a --- /dev/null +++ b/requirements/docs.txt @@ -0,0 +1,3 @@ +recommonmark~=0.6 +sphinx-rtd-theme~=0.5 +sphinx~=3.2 diff --git a/requirements/prod.txt b/requirements/prod.txt new file mode 100644 index 0000000..e69de29 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..0d1d4df --- /dev/null +++ b/setup.cfg @@ -0,0 +1,41 @@ +[tool:pytest] +addopts = -s --strict -vv --cache-clear --maxfail=1 --cov=mapply --cov-report=term --cov-report=html --cov-branch --no-cov-on-fail + +[isort] +profile = black +default_section = THIRDPARTY +known_first_party = tests + +[flake8] +ignore = D10,E203,E501,W503 +max-line-length = 88 +inline-quotes = double +docstring-convention = google + +[coverage:run] +branch = True + +[coverage:report] +exclude_lines = + pragma: no cover + +[mypy] +files = src/**/*.py +ignore_missing_imports = True +warn_no_return = False +disallow_untyped_defs = False +allow_redefinition = True + +[darglint] +strictness = short + +[tool:interrogate] +fail-under = 100 +exclude = docs,tests,setup.py,.eggs,.env,.venv,src/mapply/_version.py +verbose = 1 +quiet = false +color = true +ignore-module = true +ignore-nested-functions = true +ignore-private = true +ignore-semiprivate = true diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..de98fff --- /dev/null +++ b/setup.py @@ -0,0 +1,55 @@ +from os import path +from setuptools import setup + +here = path.abspath(path.dirname(__file__)) + +requirements_path = path.join(here, "requirements", "prod.txt") + +readme_path = path.join(here, "README.md") + + +def read_requirements(path): + try: + with open(path, mode="rt", encoding="utf-8") as fp: + return list( + filter(None, [line.split("#")[0].strip() for line in fp]) # noqa:C407 + ) + except IndexError: + raise RuntimeError("{} is broken".format(path)) + + +def read_readme(path): + with open(path, mode="rt", encoding="utf-8") as fp: + return fp.read() + + +setup( + name="mapply", + long_description=read_readme(readme_path), + long_description_content_type="text/markdown", + setup_requires=["setuptools_scm"], + install_requires=read_requirements(requirements_path), + use_scm_version={"write_to": "src/mapply/_version.py"}, + package_dir={"": "src"}, + author="ddelange", + author_email="david@delange.dev", + url="https://github.com/ddelange/mapply", + python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*", + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Utilities", + ], + keywords="pandas parallel apply map applymap multicore multiprocessing", +) diff --git a/src/mapply/__init__.py b/src/mapply/__init__.py new file mode 100644 index 0000000..afce6af --- /dev/null +++ b/src/mapply/__init__.py @@ -0,0 +1,2 @@ +# flake8: noqa:F401 +from mapply._version import version as __version__