diff --git a/.boussole.json b/.boussole.json new file mode 100644 index 000000000..65040b4c1 --- /dev/null +++ b/.boussole.json @@ -0,0 +1,8 @@ +{ + "SOURCES_PATH": "src/mawek/scss", + "TARGET_PATH": "src/mawek/templates/static/stylesheets", + "LIBRARY_PATHS": [], + "OUTPUT_STYLES": "compressed", + "SOURCE_COMMENTS": false, + "EXCLUDES": [] +} diff --git a/.flake8 b/.flake8 new file mode 100644 index 000000000..9410c7942 --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length = 88 +extend-ignore = E203,E501 diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 000000000..399324f13 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,6 @@ +[settings] +line_length = 88 +known_first_party = mawek +default_section = THIRDPARTY +multi_line_output = 3 +include_trailing_comma = True diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..57b827c70 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,43 @@ +repos: + + - repo: https://github.com/psf/black + rev: 19.10b0 + hooks: + - id: black + language_version: python3.8 + + - repo: https://github.com/timothycrosley/isort + rev: 5.0.4 + hooks: + - id: isort + files: \.py$ + + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.8.3 + hooks: + - id: flake8 + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.1.0 + hooks: + - id: check-builtin-literals + - id: check-added-large-files + - id: check-case-conflict + - id: check-toml + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + - id: forbid-new-submodules + - id: trailing-whitespace + + - repo: https://github.com/PyCQA/pydocstyle.git + rev: '5.0.2' + hooks: + - id: pydocstyle + files: src/.*\.py$ + + - repo: https://github.com/asottile/blacken-docs + rev: v1.7.0 + hooks: + - id: blacken-docs + additional_dependencies: [black==19.10b0] diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..1a5a21607 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +conduct@pradyunsg.me. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..02429d54f --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 Pradyun Gedam + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 000000000..7d8c490dc --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# mawek + +I still have to figure out what this would be. diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 000000000..0eebd15a8 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,28 @@ +# Configuration file for the Sphinx documentation builder. +# +# Full list of options can be found in the Sphinx documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# +# -- Project information ----------------------------------------------------- +# + +project = "mawek" +copyright = "2020, Pradyun Gedam" +author = "Pradyun Gedam" + +# +# -- General configuration --------------------------------------------------- +# + +extensions = ["sphinx.ext.autodoc"] +templates_path = ["_templates"] + +# +# -- Options for HTML output ------------------------------------------------- +# + +html_theme = "alabaster" + +html_static_path = ["_static"] +html_title = "mawek" diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 000000000..63e23feca --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,4 @@ +mawek +===== + +I still have to figure out what this would be. diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 000000000..2d14c3772 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,231 @@ +"""Development automation +""" +import os +import re +import subprocess +from contextlib import contextmanager +from glob import glob +from pathlib import Path +from time import sleep, time + +import nox + +PACKAGE_NAME = "mawek" +nox.options.sessions = ["lint", "test"] +nox.options.reuse_existing_virtualenvs = True + + +# +# Helpers +# +def _install_this_project_with_flit(session, *, extras=None, editable=False): + session.install("flit") + args = [] + if extras: + args.append("--extras") + args.append(",".join(extras)) + if editable: + args.append("--pth-file" if os.name == "nt" else "--symlink") + + session.run("flit", "install", "--deps=production", *args, silent=True) + + +@contextmanager +def _background_process(session, cmd): + """Run 'cmd' from the session's virtualenv, in the background""" + env = {"PATH": os.pathsep.join([session.virtualenv.bin, os.environ["PATH"]])} + + executable_name, *rest = cmd + executable = os.path.join(session.virtualenv.bin, executable_name) + command = [executable, *rest] + + session.log(" ".join(cmd) + " &") + process = subprocess.Popen(command, env=env) + try: + sleep(0.5) # just to let any "starting" output catch up. + yield + finally: + process.terminate() + + +# +# Development Sessions +# +@nox.session(name="docs-live", python="3.8") +def docs_live(session): + # Auto compile SCSS files, on change + # Auto generate documentation, on change, in src/ or docs/ + _install_this_project_with_flit(session, extras=["doc"], editable=True) + session.install("sphinx-autobuild", "boussole") + + boussle_args = ["--config", ".boussole.json"] + session.run("boussole", "compile", *boussle_args) + + with _background_process(session, ["boussole", "watch"] + boussle_args): + session.run( + "sphinx-autobuild", + "--port", + "0", + "--watch", + "src/", + "--open-browser", + "-a", + "-q", + "docs/", + "build/docs/", + ) + + +@nox.session(python="3.8") +def docs(session): + _install_this_project_with_flit(session, extras=["doc"], editable=True) + + # Generate documentation into `build/docs` + session.run("sphinx-build", "-b", "html", "-v", "docs/", "build/docs") + + +@nox.session(python="3.8") +def lint(session): + session.install("pre-commit") + + if session.posargs: + args = session.posargs + ["--all-files"] + else: + args = ["--all-files", "--show-diff-on-failure"] + + session.run("pre-commit", "run", "--all-files", *args) + + +@nox.session(python="3.8") +def test(session): + _install_this_project_with_flit(session, extras=["test"]) + + args = session.posargs or ["-n", "auto", "--cov", PACKAGE_NAME] + session.run("pytest", *args) + + +# +# Helpers (Release Automation) +# +def get_version_from_arguments(arguments): + """Checks the arguments passed to `nox -s release`. + + If there is only 1 argument that looks like a version, returns the argument. + Otherwise, returns None. + """ + if len(arguments) != 1: + return None + + version = arguments[0] + + parts = version.split(".") + if len(parts) != 3: + # Not of the form: MAJOR.MINOR.PATCH + return None + + if not all(part.isdigit() for part in parts): + # Not all segments are integers. + return None + + # All is good. + return version + + +def perform_git_checks(session, version_tag): + # Ensure we're on master branch for cutting a release. + result = subprocess.run( + ["git", "rev-parse", "--abbrev-ref", "HEAD"], + capture_output=True, + encoding="utf-8", + ) + if result.stdout != "master\n": + session.error(f"Not on master branch: {result.stdout!r}") + + # Ensure there are no uncommitted changes. + result = subprocess.run( + ["git", "status", "--porcelain"], capture_output=True, encoding="utf-8" + ) + if result.stdout: + print(result.stdout) + session.error("The working tree has uncommitted changes") + + # Ensure this tag doesn't exist already. + result = subprocess.run( + ["git", "rev-parse", version_tag], capture_output=True, encoding="utf-8" + ) + if not result.returncode: + session.error(f"Tag already exists! {version_tag} -- {result.stdout!r}") + + # Back up the current git reference, in a tag that's easy to clean up. + _release_backup_tag = "auto/release-start-" + str(int(time())) + session.run("git", "tag", _release_backup_tag, external=True) + + +def bump(session, *, version, file, kind): + session.log(f"Bump version to {version!r}") + contents = file.read_text() + new_contents = re.sub( + '__version__ = "(.+)"', f'__version__ = "{version}"', contents + ) + file.write_text(new_contents) + + session.log("git commit") + subprocess.run(["git", "add", str(file)]) + subprocess.run(["git", "commit", "-m", f"Bump for {kind}"]) + + +# +# Release Automation +# +@nox.session +def release(session): + release_version = get_version_from_arguments(session.posargs) + if not release_version: + session.error("Usage: nox -s release -- MAJOR.MINOR.PATCH") + + # Do sanity check about the state of the git repository + perform_git_checks(session, release_version) + + # Install release dependencies + session.install("twine", "flit") + version_file = Path(f"src/{PACKAGE_NAME}/__init__.py") + + # Bump for release + bump(session, version=release_version, file=version_file, kind="release") + + # Tag the release commit + session.run( + "git", + "tag", + "-s", + "-m", + f"Release {release_version}", + release_version, + external=True, + ) + + # Bump for development + major, minor, patch = map(int, release_version.split(".")) + next_version = f"{major}.{minor}.{patch + 1}.dev0" + + bump(session, version=next_version, file=version_file, kind="development") + + # Checkout the git tag + session.run("git", "checkout", "-q", release_version, external=True) + + # Build the distribution + session.run("flit", "build") + files = glob(f"dist/{PACKAGE_NAME}-{release_version}*") + assert len(files) == 2 + + # Get back out into master + session.run("git", "checkout", "-q", "master", external=True) + + # Check and upload distribution files + session.run("twine", "check", *files) + + # Upload the distribution + session.run("twine", "upload", *files) + + # Push the commits and tag + session.run("git", "push", "origin", "master", release_version, external=True) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..8e7d43fc5 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,25 @@ +[build-system] +requires = ["flit_core >=2,<4"] +build-backend = "flit_core.buildapi" + +[tool.flit.metadata] +module = "mawek" +author = "Pradyun Gedam" +author-email = "mail@pradyunsg.me" +home-page = "https://github.com/pradyunsg/mawek" +description-file = "README.md" +requires-python = ">=3.5" + +[tool.flit.metadata.requires-extra] +test = [ + "pytest", + "pytest-cov", + "pytest-xdist", +] +doc = ["sphinx"] + +[tool.flit.entrypoints] +"sphinx.html_themes" = {mawek = "mawek"} + +[tool.flit.sdist] +exclude = ["src/mawek/scss"] diff --git a/src/mawek/__init__.py b/src/mawek/__init__.py new file mode 100644 index 000000000..54c9e9730 --- /dev/null +++ b/src/mawek/__init__.py @@ -0,0 +1,3 @@ +"""I still have to figure out what this would be.""" + +__version__ = "0.1.0.dev0" diff --git a/src/mawek/scss/default.scss b/src/mawek/scss/default.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/mawek/templates/base.html b/src/mawek/templates/base.html new file mode 100644 index 000000000..496321cb7 --- /dev/null +++ b/src/mawek/templates/base.html @@ -0,0 +1,11 @@ +{%- import "!partials/_variables.jinja" as theme with context -%} + + + + + {%- include "!partials/_head.html" -%} + + + {% block body %}{% endblock %} + + diff --git a/src/mawek/templates/domainindex.html b/src/mawek/templates/domainindex.html new file mode 100644 index 000000000..94d9808cc --- /dev/null +++ b/src/mawek/templates/domainindex.html @@ -0,0 +1 @@ +{% extends "base.html" %} diff --git a/src/mawek/templates/genindex.html b/src/mawek/templates/genindex.html new file mode 100644 index 000000000..94d9808cc --- /dev/null +++ b/src/mawek/templates/genindex.html @@ -0,0 +1 @@ +{% extends "base.html" %} diff --git a/src/mawek/templates/page.html b/src/mawek/templates/page.html new file mode 100644 index 000000000..fe9cfe38d --- /dev/null +++ b/src/mawek/templates/page.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} + +{% block body -%} + {{ content }} +{%- endblock %} diff --git a/src/mawek/templates/search.html b/src/mawek/templates/search.html new file mode 100644 index 000000000..94d9808cc --- /dev/null +++ b/src/mawek/templates/search.html @@ -0,0 +1 @@ +{% extends "base.html" %} diff --git a/src/mawek/templates/theme.conf b/src/mawek/templates/theme.conf new file mode 100644 index 000000000..cf84c6c66 --- /dev/null +++ b/src/mawek/templates/theme.conf @@ -0,0 +1,3 @@ +[theme] +inherit = none +stylesheet = default.css