madduck's git repository
Every one of the projects in this repository is available at the canonical
URL git://git.madduck.net/madduck/pub/<projectpath> — see
each project's metadata for the exact URL.
All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
SSH access, as well as push access can be individually
arranged.
If you use my repositories frequently, consider adding the following
snippet to ~/.gitconfig and using the third clone URL listed for each
project:
[url "git://git.madduck.net/madduck/"]
insteadOf = madduck:
+from mypy_extensions import mypyc_attr
from pathspec import PathSpec
from pathspec import PathSpec
+from pathspec.patterns.gitwildmatch import GitWildMatchPatternError
import tomli
from black.output import err
from black.report import Report
import tomli
from black.output import err
from black.report import Report
+from black.handle_ipynb_magics import jupyter_dependencies_are_installed
if TYPE_CHECKING:
import colorama # noqa: F401
@lru_cache()
if TYPE_CHECKING:
import colorama # noqa: F401
@lru_cache()
-def find_project_root(srcs: Sequence[str]) -> Path:
+def find_project_root(srcs: Sequence[str]) -> Tuple[Path, str]:
"""Return a directory containing .git, .hg, or pyproject.toml.
That directory will be a common parent of all files and directories
"""Return a directory containing .git, .hg, or pyproject.toml.
That directory will be a common parent of all files and directories
If no directory in the tree contains a marker that would specify it's the
project root, the root of the file system is returned.
If no directory in the tree contains a marker that would specify it's the
project root, the root of the file system is returned.
+
+ Returns a two-tuple with the first element as the project root path and
+ the second element as a string describing the method by which the
+ project root was discovered.
"""
if not srcs:
srcs = [str(Path.cwd().resolve())]
"""
if not srcs:
srcs = [str(Path.cwd().resolve())]
for directory in (common_base, *common_base.parents):
if (directory / ".git").exists():
for directory in (common_base, *common_base.parents):
if (directory / ".git").exists():
+ return directory, ".git directory"
if (directory / ".hg").is_dir():
if (directory / ".hg").is_dir():
+ return directory, ".hg directory"
if (directory / "pyproject.toml").is_file():
if (directory / "pyproject.toml").is_file():
+ return directory, "pyproject.toml"
+ return directory, "file system root"
def find_pyproject_toml(path_search_start: Tuple[str, ...]) -> Optional[str]:
"""Find the absolute filepath to a pyproject.toml if it exists"""
def find_pyproject_toml(path_search_start: Tuple[str, ...]) -> Optional[str]:
"""Find the absolute filepath to a pyproject.toml if it exists"""
- path_project_root = find_project_root(path_search_start)
+ path_project_root, _ = find_project_root(path_search_start)
path_pyproject_toml = path_project_root / "pyproject.toml"
if path_pyproject_toml.is_file():
return str(path_pyproject_toml)
path_pyproject_toml = path_project_root / "pyproject.toml"
if path_pyproject_toml.is_file():
return str(path_pyproject_toml)
+@mypyc_attr(patchable=True)
def parse_pyproject_toml(path_config: str) -> Dict[str, Any]:
"""Parse a pyproject toml file, pulling out relevant parts for Black
If parsing fails, will raise a tomli.TOMLDecodeError
"""
def parse_pyproject_toml(path_config: str) -> Dict[str, Any]:
"""Parse a pyproject toml file, pulling out relevant parts for Black
If parsing fails, will raise a tomli.TOMLDecodeError
"""
- with open(path_config, encoding="utf8") as f:
+ with open(path_config, "rb") as f:
pyproject_toml = tomli.load(f)
config = pyproject_toml.get("tool", {}).get("black", {})
return {k.replace("--", "").replace("-", "_"): v for k, v in config.items()}
pyproject_toml = tomli.load(f)
config = pyproject_toml.get("tool", {}).get("black", {})
return {k.replace("--", "").replace("-", "_"): v for k, v in config.items()}
if gitignore.is_file():
with gitignore.open(encoding="utf-8") as gf:
lines = gf.readlines()
if gitignore.is_file():
with gitignore.open(encoding="utf-8") as gf:
lines = gf.readlines()
- return PathSpec.from_lines("gitwildmatch", lines)
+ try:
+ return PathSpec.from_lines("gitwildmatch", lines)
+ except GitWildMatchPatternError as e:
+ err(f"Could not parse {gitignore}: {e}")
+ raise
def normalize_path_maybe_ignore(
def normalize_path_maybe_ignore(
- path: Path, root: Path, report: Report
+ path: Path,
+ root: Path,
+ report: Optional[Report] = None,
) -> Optional[str]:
"""Normalize `path`. May return `None` if `path` was ignored.
) -> Optional[str]:
"""Normalize `path`. May return `None` if `path` was ignored.
abspath = path if path.is_absolute() else Path.cwd() / path
normalized_path = abspath.resolve().relative_to(root).as_posix()
except OSError as e:
abspath = path if path.is_absolute() else Path.cwd() / path
normalized_path = abspath.resolve().relative_to(root).as_posix()
except OSError as e:
- report.path_ignored(path, f"cannot be read because {e}")
+ if report:
+ report.path_ignored(path, f"cannot be read because {e}")
return None
except ValueError:
if path.is_symlink():
return None
except ValueError:
if path.is_symlink():
- report.path_ignored(path, f"is a symbolic link that points outside {root}")
+ if report:
+ report.path_ignored(
+ path, f"is a symbolic link that points outside {root}"
+ )
force_exclude: Optional[Pattern[str]],
report: Report,
gitignore: Optional[PathSpec],
force_exclude: Optional[Pattern[str]],
report: Report,
gitignore: Optional[PathSpec],
+ *,
+ verbose: bool,
+ quiet: bool,
) -> Iterator[Path]:
"""Generate all files under `path` whose paths are not excluded by the
`exclude_regex`, `extend_exclude`, or `force_exclude` regexes,
) -> Iterator[Path]:
"""Generate all files under `path` whose paths are not excluded by the
`exclude_regex`, `extend_exclude`, or `force_exclude` regexes,
force_exclude,
report,
gitignore + get_gitignore(child) if gitignore is not None else None,
force_exclude,
report,
gitignore + get_gitignore(child) if gitignore is not None else None,
+ verbose=verbose,
+ quiet=quiet,
+ if child.suffix == ".ipynb" and not jupyter_dependencies_are_installed(
+ verbose=verbose, quiet=quiet
+ ):
+ continue
include_match = include.search(normalized_path) if include else True
if include_match:
yield child
include_match = include.search(normalized_path) if include else True
if include_match:
yield child