diff --git a/.gitignore b/.gitignore index 642307f..9c0aec4 100644 --- a/.gitignore +++ b/.gitignore @@ -114,4 +114,4 @@ venv.bak/ dmypy.json # Pyre type checker -.pyre/ \ No newline at end of file +.pyre/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..6366604 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,52 @@ +exclude: ".venv|__pycache__|tests/dev/|tests/fixtures/" +fail_fast: false +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-added-large-files + args: ["--maxkb=500"] + - id: check-ast + - id: check-builtin-literals + - id: check-case-conflict + - id: check-toml + - id: check-yaml + - id: detect-private-key + - id: end-of-file-fixer + - id: fix-byte-order-marker + - id: fix-encoding-pragma + args: [--remove] + - id: name-tests-test + args: [--pytest-test-first] + - id: trailing-whitespace + args: [--markdown-linebreak-ext=md] + + - repo: https://github.com/asottile/pyupgrade + rev: v3.10.1 + hooks: + - id: pyupgrade + args: + - "--py38-plus" + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: "v0.0.287" + hooks: + - id: ruff + args: ["--fix-only", "--target-version=py38"] + + - repo: https://github.com/psf/black + rev: 23.7.0 + hooks: + - id: black + args: ["--target-version=py38"] + + - repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + args: ["--profile", "black", "--filter-files"] + +ci: + autoupdate_schedule: quarterly + skip: [] + submodules: false diff --git a/LICENSE b/LICENSE index b7dedcc..098ebb9 100644 --- a/LICENSE +++ b/LICENSE @@ -16,4 +16,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 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. \ No newline at end of file +SOFTWARE. diff --git a/mkdocs_git_committers_plugin_2/exclude.py b/mkdocs_git_committers_plugin_2/exclude.py index 9edf811..2d4ff7e 100644 --- a/mkdocs_git_committers_plugin_2/exclude.py +++ b/mkdocs_git_committers_plugin_2/exclude.py @@ -2,8 +2,8 @@ Module to assist exclude certain files being processed by plugin. Inspired by https://github.com/apenwarr/mkdocs-exclude """ -import os import fnmatch +import os from typing import List diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index b960a7c..e2145f7 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -1,77 +1,78 @@ -import os -import sys +import json import logging -from pprint import pprint +import os +import re +import time +from datetime import datetime from timeit import default_timer as timer -from datetime import datetime, timedelta -from mkdocs import utils as mkdocs_utils -from mkdocs.config import config_options, Config +import requests +from bs4 import BeautifulSoup as bs +from git import Commit, Repo +from mkdocs.config import config_options from mkdocs.plugins import BasePlugin - -from git import Repo, Commit -import requests, json from requests.exceptions import HTTPError -import time -import hashlib -import re -from bs4 import BeautifulSoup as bs from mkdocs_git_committers_plugin_2.exclude import exclude LOG = logging.getLogger("mkdocs.plugins." + __name__) -class GitCommittersPlugin(BasePlugin): +class GitCommittersPlugin(BasePlugin): config_scheme = ( - ('enterprise_hostname', config_options.Type(str, default='')), - ('repository', config_options.Type(str, default='')), - ('branch', config_options.Type(str, default='master')), - ('docs_path', config_options.Type(str, default='docs/')), - ('enabled', config_options.Type(bool, default=True)), - ('cache_dir', config_options.Type(str, default='.cache/plugin/git-committers')), + ("enterprise_hostname", config_options.Type(str, default="")), + ("repository", config_options.Type(str, default="")), + ("branch", config_options.Type(str, default="master")), + ("docs_path", config_options.Type(str, default="docs/")), + ("enabled", config_options.Type(bool, default=True)), + ("cache_dir", config_options.Type(str, default=".cache/plugin/git-committers")), ("exclude", config_options.Type(list, default=[])), ) def __init__(self): self.total_time = 0 - self.branch = 'master' + self.branch = "master" self.enabled = True - self.authors = dict() - self.cache_page_authors = dict() - self.exclude = list() - self.cache_date = '' + self.authors = {} + self.cache_page_authors = {} + self.exclude = [] + self.cache_date = "" def on_config(self, config): - self.enabled = self.config['enabled'] + self.enabled = self.config["enabled"] if not self.enabled: LOG.info("git-committers plugin DISABLED") return config LOG.info("git-committers plugin ENABLED") - if not self.config['repository']: + if not self.config["repository"]: LOG.error("git-committers plugin: repository not specified") return config - if self.config['enterprise_hostname'] and self.config['enterprise_hostname'] != '': - self.githuburl = "https://" + self.config['enterprise_hostname'] + "/" + if ( + self.config["enterprise_hostname"] + and self.config["enterprise_hostname"] != "" + ): + self.githuburl = "https://" + self.config["enterprise_hostname"] + "/" else: self.githuburl = "https://github.com/" self.localrepo = Repo(".") - self.branch = self.config['branch'] - self.excluded_pages = self.config['exclude'] + self.branch = self.config["branch"] + self.excluded_pages = self.config["exclude"] return config def list_contributors(self, path): - if exclude(path.lstrip(self.config['docs_path']), self.excluded_pages): + if exclude(path.lstrip(self.config["docs_path"]), self.excluded_pages): return None, None - + last_commit_date = "" path = path.replace("\\", "/") for c in Commit.iter_items(self.localrepo, self.localrepo.head, path): if not last_commit_date: # Use the last commit and get the date - last_commit_date = time.strftime("%Y-%m-%d", time.gmtime(c.authored_date)) + last_commit_date = time.strftime( + "%Y-%m-%d", time.gmtime(c.authored_date) + ) # File not committed yet if last_commit_date == "": @@ -80,68 +81,92 @@ def list_contributors(self, path): # Try to leverage the cache if path in self.cache_page_authors: - if self.cache_date and time.strptime(last_commit_date, "%Y-%m-%d") < time.strptime(self.cache_date, "%Y-%m-%d"): - return self.cache_page_authors[path]['authors'], self.cache_page_authors[path]['last_commit_date'] - - url_contribs = self.githuburl + self.config['repository'] + "/contributors-list/" + self.config['branch'] + "/" + path + if self.cache_date and time.strptime( + last_commit_date, "%Y-%m-%d" + ) < time.strptime(self.cache_date, "%Y-%m-%d"): + return ( + self.cache_page_authors[path]["authors"], + self.cache_page_authors[path]["last_commit_date"], + ) + + url_contribs = ( + self.githuburl + + self.config["repository"] + + "/contributors-list/" + + self.config["branch"] + + "/" + + path + ) LOG.info("git-committers: fetching contributors for " + path) LOG.debug(" from " + url_contribs) - authors=[] + authors = [] try: response = requests.get(url_contribs) response.raise_for_status() except HTTPError as http_err: - LOG.error(f'git-committers: HTTP error occurred: {http_err}\n(404 is normal if file is not on GitHub yet or Git submodule)') + LOG.error( + f"git-committers: HTTP error occurred: {http_err}\n(404 is normal if file is not on GitHub yet or Git submodule)" + ) except Exception as err: - LOG.error(f'git-committers: Other error occurred: {err}') + LOG.error(f"git-committers: Other error occurred: {err}") else: html = response.text # Parse the HTML soup = bs(html, "lxml") - lis = soup.find_all('li') + lis = soup.find_all("li") for li in lis: - a_tags = li.find_all('a') - login = a_tags[0]['href'].replace("/", "") + a_tags = li.find_all("a") + login = a_tags[0]["href"].replace("/", "") url = self.githuburl + login name = login - img_tags = li.find_all('img') - avatar = img_tags[0]['src'] - avatar = re.sub(r'\?.*$', '', avatar) - authors.append({'login':login, 'name': name, 'url': url, 'avatar': avatar}) + img_tags = li.find_all("img") + avatar = img_tags[0]["src"] + avatar = re.sub(r"\?.*$", "", avatar) + authors.append( + {"login": login, "name": name, "url": url, "avatar": avatar} + ) # Update global cache_page_authors - self.cache_page_authors[path] = {'last_commit_date': last_commit_date, 'authors': authors} + self.cache_page_authors[path] = { + "last_commit_date": last_commit_date, + "authors": authors, + } return authors, last_commit_date def on_page_context(self, context, page, config, nav): - context['committers'] = [] + context["committers"] = [] if not self.enabled: return context start = timer() - git_path = self.config['docs_path'] + page.file.src_path + git_path = self.config["docs_path"] + page.file.src_path authors, last_commit_date = self.list_contributors(git_path) if authors: - context['committers'] = authors + context["committers"] = authors if last_commit_date: - context['last_commit_date'] = last_commit_date + context["last_commit_date"] = last_commit_date end = timer() - self.total_time += (end - start) + self.total_time += end - start return context def on_post_build(self, config): LOG.info("git-committers: saving page authors cache file") - json_data = json.dumps({'cache_date': datetime.now().strftime("%Y-%m-%d"), 'page_authors': self.cache_page_authors}) - os.makedirs(self.config['cache_dir'], exist_ok=True) - f = open(self.config['cache_dir'] + "/page-authors.json", "w") + json_data = json.dumps( + { + "cache_date": datetime.now().strftime("%Y-%m-%d"), + "page_authors": self.cache_page_authors, + } + ) + os.makedirs(self.config["cache_dir"], exist_ok=True) + f = open(self.config["cache_dir"] + "/page-authors.json", "w") f.write(json_data) f.close() def on_pre_build(self, config): - if os.path.exists(self.config['cache_dir'] + "/page-authors.json"): + if os.path.exists(self.config["cache_dir"] + "/page-authors.json"): LOG.info("git-committers: found page authors cache file - loading it") - f = open(self.config['cache_dir'] + "/page-authors.json", "r") + f = open(self.config["cache_dir"] + "/page-authors.json") cache = json.loads(f.read()) - self.cache_date = cache['cache_date'] - self.cache_page_authors = cache['page_authors'] + self.cache_date = cache["cache_date"] + self.cache_page_authors = cache["page_authors"] f.close() diff --git a/setup.py b/setup.py index 28754d3..be26ec3 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from pathlib import Path from typing import Union -from setuptools import setup, find_packages +from setuptools import find_packages, setup # The directory containing this file HERE = Path(__file__).parent @@ -9,6 +9,7 @@ # The text of the README file README = (HERE / "README.md").read_text() + def load_requirements(requirements_files: Union[Path, list[Path]]) -> list: """Helper to load requirements list from a path or a list of paths. @@ -36,35 +37,36 @@ def load_requirements(requirements_files: Union[Path, list[Path]]) -> list: return out_requirements + setup( - name='mkdocs-git-committers-plugin-2', - version='1.2.0', - description='An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of github committers and other useful GIT info such as last modified date', + name="mkdocs-git-committers-plugin-2", + version="1.2.0", + description="An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of github committers and other useful GIT info such as last modified date", long_description=README, long_description_content_type="text/markdown", - keywords='mkdocs pdf github', - url='https://github.com/ojacques/mkdocs-git-committers-plugin-2/', - author='Byrne Reese, Olivier Jacques', - author_email='byrne@majordojo.com, ojacques2@gmail.com', - license='MIT', - python_requires='>=3.8,<4', + keywords="mkdocs pdf github", + url="https://github.com/ojacques/mkdocs-git-committers-plugin-2/", + author="Byrne Reese, Olivier Jacques", + author_email="byrne@majordojo.com, ojacques2@gmail.com", + license="MIT", + python_requires=">=3.8,<4", install_requires=load_requirements(HERE / "requirements.txt"), classifiers=[ - 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'Intended Audience :: Information Technology', - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11' + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", ], packages=find_packages(), entry_points={ - 'mkdocs.plugins': [ - 'git-committers = mkdocs_git_committers_plugin_2.plugin:GitCommittersPlugin' + "mkdocs.plugins": [ + "git-committers = mkdocs_git_committers_plugin_2.plugin:GitCommittersPlugin" ] - } + }, )