From ff3772002fe52db645dddefbeae34f61e1bbb419 Mon Sep 17 00:00:00 2001 From: DYNOSuprovo Date: Tue, 29 Sep 2026 13:45:51 +0530 Subject: [PATCH] sdist: match dotfiles in file pattern globbing Switch FilePatterns globbing in flit_core to use Path.glob so that recursive patterns like **/*.swp correctly match hidden dotfiles. Normalize pattern syntax for pathlib compatibility and update default sdist exclude **.pyc to **/*.pyc. Signed-off-by: DYNOSuprovo --- flit_core/flit_core/config.py | 2 +- flit_core/flit_core/sdist.py | 13 +++++++-- flit_core/tests_core/test_sdist.py | 46 ++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/flit_core/flit_core/config.py b/flit_core/flit_core/config.py index 31d1b5d3..aa4b1092 100644 --- a/flit_core/flit_core/config.py +++ b/flit_core/flit_core/config.py @@ -156,7 +156,7 @@ def prep_toml_config(d, path): ) exclude = [ "**/__pycache__", - "**.pyc", + "**/*.pyc", ] + dtool['sdist'].get('exclude', []) loaded_cfg.sdist_exclude_patterns = _check_glob_patterns( exclude, 'exclude' diff --git a/flit_core/flit_core/sdist.py b/flit_core/flit_core/sdist.py index ff1c52ea..9cb6e4bb 100644 --- a/flit_core/flit_core/sdist.py +++ b/flit_core/flit_core/sdist.py @@ -1,6 +1,5 @@ from collections import defaultdict from copy import copy -from glob import glob from gzip import GzipFile import io import logging @@ -8,6 +7,7 @@ import os.path as osp from pathlib import Path from posixpath import join as pjoin +import re import tarfile from . import common @@ -42,8 +42,17 @@ def __init__(self, patterns, basedir): self.dirs = set() self.files = set() + base_path = Path(basedir) for pattern in patterns: - for path in sorted(glob(osp.join(basedir, pattern), recursive=True)): + norm_pattern = pattern.lstrip('/\\') + norm_pattern = re.sub(r'(^|[/\\])\*\*([a-zA-Z0-9_.*]+)', r'\g<1>**/*\2', norm_pattern) + parts = [] + for part in norm_pattern.replace('\\', '/').split('/'): + if '**' in part and part != '**': + part = part.replace('**', '*') + parts.append(part) + norm_pattern = '/'.join(parts) + for path in sorted(base_path.glob(norm_pattern)): rel = osp.relpath(path, basedir) if osp.isdir(path): self.dirs.add(rel) diff --git a/flit_core/tests_core/test_sdist.py b/flit_core/tests_core/test_sdist.py index 2a4401cb..54caa9dd 100644 --- a/flit_core/tests_core/test_sdist.py +++ b/flit_core/tests_core/test_sdist.py @@ -82,3 +82,49 @@ def test_license_inside_src(tmp_path): ) assert lic_count == 1 + + +def test_recursive_glob_dotfiles(tmp_path): + (tmp_path / 'pkg').mkdir() + (tmp_path / 'pkg' / '__init__.py').write_text('"""A sample package"""\n__version__ = "0.1"\n') + (tmp_path / 'pkg' / 'sub').mkdir() + (tmp_path / 'pkg' / 'sub' / '.hidden.swp').write_text('swap') + (tmp_path / 'pkg' / '.root.swp').write_text('swap') + (tmp_path / 'pkg' / 'normal.txt').write_text('normal') + + patterns = sdist.FilePatterns(['**/*.swp'], str(tmp_path)) + assert patterns.match_file(osp.join('pkg', '.root.swp')) + assert patterns.match_file(osp.join('pkg', 'sub', '.hidden.swp')) + assert not patterns.match_file(osp.join('pkg', 'normal.txt')) + + +def test_sdist_exclude_dotfiles(tmp_path): + pkg_dir = tmp_path / 'pkg' + pkg_dir.mkdir() + (pkg_dir / '__init__.py').write_text('"""A sample package"""\n__version__ = "0.1"\n') + (pkg_dir / 'sub').mkdir() + (pkg_dir / 'sub' / '.editor.swp').write_text('swap') + (pkg_dir / '.main.swp').write_text('swap') + (pkg_dir / 'module.py').write_text('x = 1\n') + + pyproject = tmp_path / 'pyproject.toml' + pyproject.write_text(""" +[build-system] +requires = ["flit_core >=3.2,<4"] +build-backend = "flit_core.buildapi" + +[project] +name = "pkg" +authors = [{name = "Test"}] +dynamic = ["version", "description"] + +[tool.flit.sdist] +exclude = ["**/*.swp"] +""") + + builder = sdist.SdistBuilder.from_ini_path(pyproject) + files = builder.apply_includes_excludes(builder.select_files()) + + assert osp.join('pkg', '.main.swp') not in files + assert osp.join('pkg', 'sub', '.editor.swp') not in files + assert osp.join('pkg', 'module.py') in files