mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2024-06-14 11:52:32 +03:00
Added exclusion logic for info plugin
This commit is contained in:
parent
64a8b6a383
commit
79129d5cde
40
material/plugins/info/info.gitignore
Normal file
40
material/plugins/info/info.gitignore
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Custom .gitignore-like file
|
||||||
|
# The difference is that those are https://docs.python.org/3/library/re.html
|
||||||
|
# regex patterns, that will be compared against directory and file names
|
||||||
|
# case-sensitively.
|
||||||
|
|
||||||
|
# Additional info:
|
||||||
|
# The paths will be always in POSIX format.
|
||||||
|
# Each directory path will have a / at the end to make it easier to
|
||||||
|
# distinguish them from files.
|
||||||
|
|
||||||
|
|
||||||
|
# Patterns for dynamic or custom paths like Virtual Environments (venv)
|
||||||
|
# or build site directories are created during plugin runtime.
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
# Python cache directory
|
||||||
|
.*__pycache__/
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
|
||||||
|
.*\.DS_Store
|
||||||
|
|
||||||
|
# .dotfiles in the root directory
|
||||||
|
|
||||||
|
^/\.[^/]+$
|
||||||
|
|
||||||
|
# Generated files and folders
|
||||||
|
|
||||||
|
^/.*\.zip
|
||||||
|
|
||||||
|
# Allow .github or .devcontainer directories
|
||||||
|
# Exclude .cache files and folders
|
||||||
|
# Exclude known IDE directories
|
||||||
|
|
||||||
|
.*\.cache/?
|
||||||
|
^/\.vscode/
|
||||||
|
^/\.vs/
|
||||||
|
^/\.idea/
|
@ -23,6 +23,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import requests
|
import requests
|
||||||
|
import site
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from colorama import Fore, Style
|
from colorama import Fore, Style
|
||||||
@ -31,6 +32,7 @@ from io import BytesIO
|
|||||||
from markdown.extensions.toc import slugify
|
from markdown.extensions.toc import slugify
|
||||||
from mkdocs.plugins import BasePlugin, event_priority
|
from mkdocs.plugins import BasePlugin, event_priority
|
||||||
from mkdocs.utils import get_theme_dir
|
from mkdocs.utils import get_theme_dir
|
||||||
|
import regex
|
||||||
from zipfile import ZipFile, ZIP_DEFLATED
|
from zipfile import ZipFile, ZIP_DEFLATED
|
||||||
|
|
||||||
from .config import InfoConfig
|
from .config import InfoConfig
|
||||||
@ -49,6 +51,9 @@ class InfoPlugin(BasePlugin[InfoConfig]):
|
|||||||
# Initialize incremental builds
|
# Initialize incremental builds
|
||||||
self.is_serve = False
|
self.is_serve = False
|
||||||
|
|
||||||
|
# Initialize empty members
|
||||||
|
self.exclusion_patterns = []
|
||||||
|
|
||||||
# Determine whether we're serving the site
|
# Determine whether we're serving the site
|
||||||
def on_startup(self, *, command, dirty):
|
def on_startup(self, *, command, dirty):
|
||||||
self.is_serve = command == "serve"
|
self.is_serve = command == "serve"
|
||||||
@ -111,12 +116,42 @@ class InfoPlugin(BasePlugin[InfoConfig]):
|
|||||||
example, _ = os.path.splitext(example)
|
example, _ = os.path.splitext(example)
|
||||||
example = "-".join([present, slugify(example, "-")])
|
example = "-".join([present, slugify(example, "-")])
|
||||||
|
|
||||||
|
# Load exclusion patterns
|
||||||
|
self.exclusion_patterns = _load_exclusion_patterns()
|
||||||
|
|
||||||
|
# Exclude the site_dir at project root
|
||||||
|
if config.site_dir.startswith(os.getcwd()):
|
||||||
|
self.exclusion_patterns.append(_resolve_pattern(config.site_dir))
|
||||||
|
|
||||||
|
# Exclude the site-packages directory
|
||||||
|
for path in site.getsitepackages():
|
||||||
|
if path.startswith(os.getcwd()):
|
||||||
|
self.exclusion_patterns.append(_resolve_pattern(path))
|
||||||
|
|
||||||
# Create self-contained example from project
|
# Create self-contained example from project
|
||||||
files: list[str] = []
|
files: list[str] = []
|
||||||
with ZipFile(archive, "a", ZIP_DEFLATED, False) as f:
|
with ZipFile(archive, "a", ZIP_DEFLATED, False) as f:
|
||||||
for abs_root, dirnames, filenames in os.walk(os.getcwd()):
|
for abs_root, dirnames, filenames in os.walk(os.getcwd()):
|
||||||
|
# Prune the folders in-place to prevent
|
||||||
|
# scanning excluded folders
|
||||||
|
for name in list(dirnames):
|
||||||
|
path = os.path.join(abs_root, name)
|
||||||
|
if self._is_excluded(_resolve_pattern(path)):
|
||||||
|
dirnames.remove(name)
|
||||||
|
continue
|
||||||
|
# Multi-language setup from #2346 separates the
|
||||||
|
# language config, so each mkdocs.yml file is
|
||||||
|
# unaware of other site_dir directories. Therefore,
|
||||||
|
# we add this with the assumption a site_dir contains
|
||||||
|
# the sitemap file.
|
||||||
|
sitemap_gz = os.path.join(path, "sitemap.xml.gz")
|
||||||
|
if os.path.exists(sitemap_gz):
|
||||||
|
log.debug(f"Excluded site_dir: {path}")
|
||||||
|
dirnames.remove(name)
|
||||||
for name in filenames:
|
for name in filenames:
|
||||||
path = os.path.join(abs_root, name)
|
path = os.path.join(abs_root, name)
|
||||||
|
if self._is_excluded(_resolve_pattern(path)):
|
||||||
|
continue
|
||||||
path = os.path.relpath(path, os.path.curdir)
|
path = os.path.relpath(path, os.path.curdir)
|
||||||
f.write(path, os.path.join(example, path))
|
f.write(path, os.path.join(example, path))
|
||||||
|
|
||||||
@ -225,6 +260,15 @@ class InfoPlugin(BasePlugin[InfoConfig]):
|
|||||||
if self.config.archive_stop_on_violation:
|
if self.config.archive_stop_on_violation:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Exclude files, which we don't want in our zip file
|
||||||
|
def _is_excluded(self, posix_path: str) -> bool:
|
||||||
|
for pattern in self.exclusion_patterns:
|
||||||
|
if regex.match(pattern, posix_path):
|
||||||
|
log.debug(f"Excluded pattern '{pattern}': {posix_path}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Helper functions
|
# Helper functions
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@ -239,6 +283,34 @@ def _size(value, factor = 1):
|
|||||||
return f"{color}{value:3.1f} {unit}"
|
return f"{color}{value:3.1f} {unit}"
|
||||||
value /= 1000.0
|
value /= 1000.0
|
||||||
|
|
||||||
|
# Load info.gitignore, ignore any empty lines or # comments
|
||||||
|
def _load_exclusion_patterns(path: str = None):
|
||||||
|
if path is None:
|
||||||
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
path = os.path.join(path, "info.gitignore")
|
||||||
|
|
||||||
|
with open(path, encoding = "utf-8") as file:
|
||||||
|
lines = map(str.strip, file.readlines())
|
||||||
|
|
||||||
|
return [line for line in lines if line and not line.startswith("#")]
|
||||||
|
|
||||||
|
# For the pattern matching it is best to remove the CWD
|
||||||
|
# prefix and keep only the relative root of the reproduction.
|
||||||
|
# Additionally, as the patterns are in POSIX format,
|
||||||
|
# assure that the path is also in POSIX format.
|
||||||
|
# Side-effect: It appends "/" for directory patterns.
|
||||||
|
def _resolve_pattern(abspath: str):
|
||||||
|
path = abspath.replace(os.getcwd(), "", 1).replace(os.sep, "/")
|
||||||
|
|
||||||
|
if not path:
|
||||||
|
return "/"
|
||||||
|
|
||||||
|
# Check abspath, as the file needs to exist
|
||||||
|
if not os.path.isfile(abspath):
|
||||||
|
return path.rstrip("/") + "/"
|
||||||
|
|
||||||
|
return path
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Data
|
# Data
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
40
src/plugins/info/info.gitignore
Normal file
40
src/plugins/info/info.gitignore
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Custom .gitignore-like file
|
||||||
|
# The difference is that those are https://docs.python.org/3/library/re.html
|
||||||
|
# regex patterns, that will be compared against directory and file names
|
||||||
|
# case-sensitively.
|
||||||
|
|
||||||
|
# Additional info:
|
||||||
|
# The paths will be always in POSIX format.
|
||||||
|
# Each directory path will have a / at the end to make it easier to
|
||||||
|
# distinguish them from files.
|
||||||
|
|
||||||
|
|
||||||
|
# Patterns for dynamic or custom paths like Virtual Environments (venv)
|
||||||
|
# or build site directories are created during plugin runtime.
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
# Python cache directory
|
||||||
|
.*__pycache__/
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
|
||||||
|
.*\.DS_Store
|
||||||
|
|
||||||
|
# .dotfiles in the root directory
|
||||||
|
|
||||||
|
^/\.[^/]+$
|
||||||
|
|
||||||
|
# Generated files and folders
|
||||||
|
|
||||||
|
^/.*\.zip
|
||||||
|
|
||||||
|
# Allow .github or .devcontainer directories
|
||||||
|
# Exclude .cache files and folders
|
||||||
|
# Exclude known IDE directories
|
||||||
|
|
||||||
|
.*\.cache/?
|
||||||
|
^/\.vscode/
|
||||||
|
^/\.vs/
|
||||||
|
^/\.idea/
|
@ -23,6 +23,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import requests
|
import requests
|
||||||
|
import site
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from colorama import Fore, Style
|
from colorama import Fore, Style
|
||||||
@ -31,6 +32,7 @@ from io import BytesIO
|
|||||||
from markdown.extensions.toc import slugify
|
from markdown.extensions.toc import slugify
|
||||||
from mkdocs.plugins import BasePlugin, event_priority
|
from mkdocs.plugins import BasePlugin, event_priority
|
||||||
from mkdocs.utils import get_theme_dir
|
from mkdocs.utils import get_theme_dir
|
||||||
|
import regex
|
||||||
from zipfile import ZipFile, ZIP_DEFLATED
|
from zipfile import ZipFile, ZIP_DEFLATED
|
||||||
|
|
||||||
from .config import InfoConfig
|
from .config import InfoConfig
|
||||||
@ -49,6 +51,9 @@ class InfoPlugin(BasePlugin[InfoConfig]):
|
|||||||
# Initialize incremental builds
|
# Initialize incremental builds
|
||||||
self.is_serve = False
|
self.is_serve = False
|
||||||
|
|
||||||
|
# Initialize empty members
|
||||||
|
self.exclusion_patterns = []
|
||||||
|
|
||||||
# Determine whether we're serving the site
|
# Determine whether we're serving the site
|
||||||
def on_startup(self, *, command, dirty):
|
def on_startup(self, *, command, dirty):
|
||||||
self.is_serve = command == "serve"
|
self.is_serve = command == "serve"
|
||||||
@ -111,12 +116,42 @@ class InfoPlugin(BasePlugin[InfoConfig]):
|
|||||||
example, _ = os.path.splitext(example)
|
example, _ = os.path.splitext(example)
|
||||||
example = "-".join([present, slugify(example, "-")])
|
example = "-".join([present, slugify(example, "-")])
|
||||||
|
|
||||||
|
# Load exclusion patterns
|
||||||
|
self.exclusion_patterns = _load_exclusion_patterns()
|
||||||
|
|
||||||
|
# Exclude the site_dir at project root
|
||||||
|
if config.site_dir.startswith(os.getcwd()):
|
||||||
|
self.exclusion_patterns.append(_resolve_pattern(config.site_dir))
|
||||||
|
|
||||||
|
# Exclude the site-packages directory
|
||||||
|
for path in site.getsitepackages():
|
||||||
|
if path.startswith(os.getcwd()):
|
||||||
|
self.exclusion_patterns.append(_resolve_pattern(path))
|
||||||
|
|
||||||
# Create self-contained example from project
|
# Create self-contained example from project
|
||||||
files: list[str] = []
|
files: list[str] = []
|
||||||
with ZipFile(archive, "a", ZIP_DEFLATED, False) as f:
|
with ZipFile(archive, "a", ZIP_DEFLATED, False) as f:
|
||||||
for abs_root, dirnames, filenames in os.walk(os.getcwd()):
|
for abs_root, dirnames, filenames in os.walk(os.getcwd()):
|
||||||
|
# Prune the folders in-place to prevent
|
||||||
|
# scanning excluded folders
|
||||||
|
for name in list(dirnames):
|
||||||
|
path = os.path.join(abs_root, name)
|
||||||
|
if self._is_excluded(_resolve_pattern(path)):
|
||||||
|
dirnames.remove(name)
|
||||||
|
continue
|
||||||
|
# Multi-language setup from #2346 separates the
|
||||||
|
# language config, so each mkdocs.yml file is
|
||||||
|
# unaware of other site_dir directories. Therefore,
|
||||||
|
# we add this with the assumption a site_dir contains
|
||||||
|
# the sitemap file.
|
||||||
|
sitemap_gz = os.path.join(path, "sitemap.xml.gz")
|
||||||
|
if os.path.exists(sitemap_gz):
|
||||||
|
log.debug(f"Excluded site_dir: {path}")
|
||||||
|
dirnames.remove(name)
|
||||||
for name in filenames:
|
for name in filenames:
|
||||||
path = os.path.join(abs_root, name)
|
path = os.path.join(abs_root, name)
|
||||||
|
if self._is_excluded(_resolve_pattern(path)):
|
||||||
|
continue
|
||||||
path = os.path.relpath(path, os.path.curdir)
|
path = os.path.relpath(path, os.path.curdir)
|
||||||
f.write(path, os.path.join(example, path))
|
f.write(path, os.path.join(example, path))
|
||||||
|
|
||||||
@ -225,6 +260,15 @@ class InfoPlugin(BasePlugin[InfoConfig]):
|
|||||||
if self.config.archive_stop_on_violation:
|
if self.config.archive_stop_on_violation:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Exclude files, which we don't want in our zip file
|
||||||
|
def _is_excluded(self, posix_path: str) -> bool:
|
||||||
|
for pattern in self.exclusion_patterns:
|
||||||
|
if regex.match(pattern, posix_path):
|
||||||
|
log.debug(f"Excluded pattern '{pattern}': {posix_path}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Helper functions
|
# Helper functions
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@ -239,6 +283,34 @@ def _size(value, factor = 1):
|
|||||||
return f"{color}{value:3.1f} {unit}"
|
return f"{color}{value:3.1f} {unit}"
|
||||||
value /= 1000.0
|
value /= 1000.0
|
||||||
|
|
||||||
|
# Load info.gitignore, ignore any empty lines or # comments
|
||||||
|
def _load_exclusion_patterns(path: str = None):
|
||||||
|
if path is None:
|
||||||
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
path = os.path.join(path, "info.gitignore")
|
||||||
|
|
||||||
|
with open(path, encoding = "utf-8") as file:
|
||||||
|
lines = map(str.strip, file.readlines())
|
||||||
|
|
||||||
|
return [line for line in lines if line and not line.startswith("#")]
|
||||||
|
|
||||||
|
# For the pattern matching it is best to remove the CWD
|
||||||
|
# prefix and keep only the relative root of the reproduction.
|
||||||
|
# Additionally, as the patterns are in POSIX format,
|
||||||
|
# assure that the path is also in POSIX format.
|
||||||
|
# Side-effect: It appends "/" for directory patterns.
|
||||||
|
def _resolve_pattern(abspath: str):
|
||||||
|
path = abspath.replace(os.getcwd(), "", 1).replace(os.sep, "/")
|
||||||
|
|
||||||
|
if not path:
|
||||||
|
return "/"
|
||||||
|
|
||||||
|
# Check abspath, as the file needs to exist
|
||||||
|
if not os.path.isfile(abspath):
|
||||||
|
return path.rstrip("/") + "/"
|
||||||
|
|
||||||
|
return path
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Data
|
# Data
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
Loading…
Reference in New Issue
Block a user