Skip to content

Commit

Permalink
Added exclusion logic for info plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilkrzyskow authored and squidfunk committed Mar 5, 2024
1 parent 64a8b6a commit 79129d5
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 0 deletions.
40 changes: 40 additions & 0 deletions material/plugins/info/info.gitignore
Original file line number Diff line number Diff line change
@@ -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/
72 changes: 72 additions & 0 deletions material/plugins/info/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import os
import platform
import requests
import site
import sys

from colorama import Fore, Style
Expand All @@ -31,6 +32,7 @@
from markdown.extensions.toc import slugify
from mkdocs.plugins import BasePlugin, event_priority
from mkdocs.utils import get_theme_dir
import regex
from zipfile import ZipFile, ZIP_DEFLATED

from .config import InfoConfig
Expand All @@ -49,6 +51,9 @@ def __init__(self, *args, **kwargs):
# Initialize incremental builds
self.is_serve = False

# Initialize empty members
self.exclusion_patterns = []

# Determine whether we're serving the site
def on_startup(self, *, command, dirty):
self.is_serve = command == "serve"
Expand Down Expand Up @@ -111,12 +116,42 @@ def on_config(self, config):
example, _ = os.path.splitext(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
files: list[str] = []
with ZipFile(archive, "a", ZIP_DEFLATED, False) as f:
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:
path = os.path.join(abs_root, name)
if self._is_excluded(_resolve_pattern(path)):
continue
path = os.path.relpath(path, os.path.curdir)
f.write(path, os.path.join(example, path))

Expand Down Expand Up @@ -225,6 +260,15 @@ def _help_on_customizations_and_exit(self):
if self.config.archive_stop_on_violation:
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
# -----------------------------------------------------------------------------
Expand All @@ -239,6 +283,34 @@ def _size(value, factor = 1):
return f"{color}{value:3.1f} {unit}"
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
# -----------------------------------------------------------------------------
Expand Down
40 changes: 40 additions & 0 deletions src/plugins/info/info.gitignore
Original file line number Diff line number Diff line change
@@ -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/
72 changes: 72 additions & 0 deletions src/plugins/info/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import os
import platform
import requests
import site
import sys

from colorama import Fore, Style
Expand All @@ -31,6 +32,7 @@
from markdown.extensions.toc import slugify
from mkdocs.plugins import BasePlugin, event_priority
from mkdocs.utils import get_theme_dir
import regex
from zipfile import ZipFile, ZIP_DEFLATED

from .config import InfoConfig
Expand All @@ -49,6 +51,9 @@ def __init__(self, *args, **kwargs):
# Initialize incremental builds
self.is_serve = False

# Initialize empty members
self.exclusion_patterns = []

# Determine whether we're serving the site
def on_startup(self, *, command, dirty):
self.is_serve = command == "serve"
Expand Down Expand Up @@ -111,12 +116,42 @@ def on_config(self, config):
example, _ = os.path.splitext(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
files: list[str] = []
with ZipFile(archive, "a", ZIP_DEFLATED, False) as f:
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:
path = os.path.join(abs_root, name)
if self._is_excluded(_resolve_pattern(path)):
continue
path = os.path.relpath(path, os.path.curdir)
f.write(path, os.path.join(example, path))

Expand Down Expand Up @@ -225,6 +260,15 @@ def _help_on_customizations_and_exit(self):
if self.config.archive_stop_on_violation:
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
# -----------------------------------------------------------------------------
Expand All @@ -239,6 +283,34 @@ def _size(value, factor = 1):
return f"{color}{value:3.1f} {unit}"
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
# -----------------------------------------------------------------------------
Expand Down

0 comments on commit 79129d5

Please sign in to comment.