diff --git a/.bumpversion.cfg b/.bumpversion.cfg index f8280ed8..3fb51ecf 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.6.0 +current_version = 3.7.0 commit = True tag = False diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 565a68ce..5fa946ba 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,13 +1,25 @@ name: Test -on: [push, pull_request] +on: + push: + branches: + - main + pull_request: + +concurrency: + group: >- + ${{ github.workflow }}- + ${{ github.ref_type }}- + ${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true jobs: test: - runs-on: ubuntu-latest + runs-on: ${{ matrix.platform }} strategy: matrix: - python-version: [ 3.6, 3.7, 3.8, 3.9, ] + platform: ["ubuntu-latest", "windows-latest"] + python-version: [ "3.6", "3.7", "3.8", "3.9", "3.10" ] steps: - uses: actions/checkout@v2 diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 08ef1a6c..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,16 +0,0 @@ -environment: - matrix: - - TOXENV: "py37" - TOX_APPVEYOR_X64: "1" - - TOXENV: "py36" - TOX_APPVEYOR_X64: "0" - -# The Python version here doesn't matter (except that the commands have the same), -# as Tox will set up an environment using the Python specified above. -install: - - py -3.7 -m pip install tox tox-appveyor - -test_script: - - py -3.7 -m tox - -build: off diff --git a/doc/bootstrap.rst b/doc/bootstrap.rst index 02da8945..19d7d923 100644 --- a/doc/bootstrap.rst +++ b/doc/bootstrap.rst @@ -13,24 +13,29 @@ from source? The key piece is ``flit_core``. This is a package which can build itself using nothing except Python and the standard library. From an unpacked source archive, -you can run ``python build_dists.py``, of which the crucial part is:: +you can make a wheel by running:: - from flit_core import buildapi - whl_fname = buildapi.build_wheel('dist/') - print(os.path.join('dist', whl_fname)) + python -m flit_core.wheel -This produces a ``.whl`` wheel file, which you can unzip into your -``site-packages`` folder (or equivalent) to make ``flit_core`` available for -building other packages. (You could also just copy ``flit_core`` from the -source directory, but without the ``.dist-info`` folder, tools like pip won't -know that it's installed.) +And then you can install this wheel with the ``bootstrap_install.py`` script +included in the sdist (or by unzipping it to the correct directory):: + + # Install to site-packages for this Python: + python bootstrap_install.py dist/flit_core-*.whl + + # Install somewhere else: + python bootstrap_install.py --installdir /path/to/site-packages dist/flit_core-*.whl As of version 3.6, flit_core bundles the ``tomli`` TOML parser, to avoid a dependency cycle. If you need to unbundle it, you will need to special-case installing flit_core and/or tomli to get around that cycle. -I recommend that you get the `build `_ and -`installer `_ packages (and their -dependencies) installed as the goal of the bootstrapping phase. These tools -together can be used to install any other Python packages: ``build`` to create -wheels and ``installer`` to install them. +After ``flit_core``, I recommend that you get `installer +`_ set up. You can use +``python -m flit_core.wheel`` again to make a wheel, and then use installer +itself (from the source directory) to install it. + +After that, you probably want to get `build `_ +and its dependencies installed as the goal of the bootstrapping phase. You can +then use ``build`` to create wheels of any other Python packages, and +``installer`` to install them. diff --git a/doc/conf.py b/doc/conf.py index 2837ce50..364334dc 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -57,7 +57,7 @@ # built documents. # # The short X.Y version. -version = '3.6.0' +version = '3.7.0' # The full version, including alpha/beta/rc tags. release = version #+ '.1' diff --git a/doc/history.rst b/doc/history.rst index e3e6a126..343cd163 100644 --- a/doc/history.rst +++ b/doc/history.rst @@ -1,6 +1,21 @@ Release history =============== +Version 3.7 +----------- + +- Support for :ref:`external data files ` such + as man pages or Jupyter extension support files (:ghpull:`510`). +- Project names are now lowercase in wheel filenames and ``.dist-info`` folder + names, in line with the specifications (:ghpull:`498`). +- Improved support for :doc:`bootstrapping ` a Python environment, + e.g. for downstream packagers (:ghpull:`511`). ``flit_core.wheel`` is usable + with ``python -m`` to create wheels before the `build `_ + tool is available, and ``flit_core`` sdists also include a script to install + itself from a wheel before `installer `_ + is available. +- Use newer importlib APIs, fixing some deprecation warnings (:ghpull:`499`). + Version 3.6 ----------- diff --git a/doc/pyproject_toml.rst b/doc/pyproject_toml.rst index 6ef305ab..388e41eb 100644 --- a/doc/pyproject_toml.rst +++ b/doc/pyproject_toml.rst @@ -417,4 +417,52 @@ These paths: Exclusions have priority over inclusions. +.. note:: + + If you are not using :ref:`build_cmd` but ``flit_core`` via another build + frontend, Flit doesn't doesn't check the VCS for files to include but instead + builds a 'minimal' sdist (which includes the files necessary to build a wheel). + You'll have to adapt your inclusion/exclusion rules to achieve the same result + as you'd get with :ref:`build_cmd`. + +.. _pyproject_toml_external_data: + +External data section +--------------------- + +.. versionadded:: 3.7 + +Data files which your code will use should go inside the Python package folder. +Flit will package these with no special configuration. + +However, sometimes it's useful to package external files for system integration, +such as man pages or files defining a Jupyter extension. To do this, arrange +the files within a directory such as ``data``, next to your ``pyproject.toml`` +file, and add a section like this: + +.. code-block:: toml + + [tool.flit.external-data] + directory = "data" + +Paths within this directory are typically installed to corresponding paths under +a prefix (such as a virtualenv directory). E.g. you might save a man page for a +script as ``(data)/share/man/man1/foo.1``. + +Whether these files are detected by the systems they're meant to integrate with +depends on how your package is installed and how those systems are configured. +For instance, installing in a virtualenv usually doesn't affect anything outside +that environment. Don't rely on these files being picked up unless you have +close control of how the package will be installed. + +If you install a package with ``flit install --symlink``, a symlink is made +for each file in the external data directory. Otherwise (including development +installs with ``pip install -e``), these files are copied to their destination, +so changes here won't take effect until you reinstall the package. + +.. note:: + + For users coming from setuptools: external data corresponds to setuptools' + ``data_files`` parameter, although setuptools offers more flexibility. + .. _environment marker: https://www.python.org/dev/peps/pep-0508/#environment-markers diff --git a/flit/__init__.py b/flit/__init__.py index 8af9b34b..24b23553 100644 --- a/flit/__init__.py +++ b/flit/__init__.py @@ -12,7 +12,7 @@ from .config import ConfigError from .log import enable_colourful_output -__version__ = '3.6.0' +__version__ = '3.7.0' log = logging.getLogger(__name__) @@ -82,12 +82,11 @@ def main(argv=None): parser_build.add_argument('--setup-py', action='store_true', help=("Generate a setup.py file in the sdist. " "The sdist will work with older tools that predate PEP 517. " - "This is the default for now, but will change in a future version." ) ) parser_build.add_argument('--no-setup-py', action='store_true', - help=("Don't generate a setup.py file in the sdist. " + help=("Don't generate a setup.py file in the sdist. This is the default. " "The sdist will only work with tools that support PEP 517, " "but the wheel will still be usable by any compatible tool." ) diff --git a/flit/install.py b/flit/install.py index fe095d65..3ea9a4bf 100644 --- a/flit/install.py +++ b/flit/install.py @@ -196,6 +196,17 @@ def install_scripts(self, script_defs, scripts_dir): self.installed_files.append(cmd_file) + def install_data_dir(self, target_data_dir): + for src_path in common.walk_data_dir(self.ini_info.data_directory): + rel_path = os.path.relpath(src_path, self.ini_info.data_directory) + dst_path = os.path.join(target_data_dir, rel_path) + os.makedirs(os.path.dirname(dst_path), exist_ok=True) + if self.symlink: + os.symlink(os.path.realpath(src_path), dst_path) + else: + shutil.copy2(src_path, dst_path) + self.installed_files.append(dst_path) + def _record_installed_directory(self, path): for dirpath, dirnames, files in os.walk(path): for f in files: @@ -332,6 +343,8 @@ def install_directly(self): scripts = self.ini_info.entrypoints.get('console_scripts', {}) self.install_scripts(scripts, dirs['scripts']) + self.install_data_dir(dirs['data']) + self.write_dist_info(dirs['purelib']) def install_with_pip(self): diff --git a/flit_core/bootstrap_install.py b/flit_core/bootstrap_install.py new file mode 100644 index 00000000..e04a007e --- /dev/null +++ b/flit_core/bootstrap_install.py @@ -0,0 +1,48 @@ +"""Install flit_core without using any other tools. + +Normally, you would install flit_core with pip like any other Python package. +This script is meant to help with 'bootstrapping' other packaging +systems, where you may need flit_core to build other packaging tools. + +Use 'python -m flit_core.wheel' to make a wheel, then: + + python bootstrap_install.py flit_core-3.6.0-py3-none-any.whl + +To install for something other than the Python running the script, pass a +site-packages or equivalent directory with the --installdir option. +""" +import argparse +import sys +import sysconfig +from pathlib import Path +from zipfile import ZipFile + +def extract_wheel(whl_path, dest): + print("Installing to", dest) + with ZipFile(whl_path) as zf: + zf.extractall(dest) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + 'wheel', + type=Path, + help=f'flit_core wheel to install (.whl file)', + ) + purelib = Path(sysconfig.get_path('purelib')).resolve() + parser.add_argument( + '--installdir', + '-i', + type=Path, + default=purelib, + help=f'installdir directory (defaults to {purelib})', + ) + + args = parser.parse_args() + + if not args.wheel.name.startswith('flit_core-'): + sys.exit("Use this script only for flit_core wheels") + if not args.installdir.is_dir(): + sys.exit(f"{args.installdir} is not a directory") + + extract_wheel(args.wheel, args.installdir) diff --git a/flit_core/flit_core/__init__.py b/flit_core/flit_core/__init__.py index 0314577d..c227699a 100644 --- a/flit_core/flit_core/__init__.py +++ b/flit_core/flit_core/__init__.py @@ -4,4 +4,4 @@ All the convenient development features live in the main 'flit' package. """ -__version__ = '3.6.0' +__version__ = '3.7.0' diff --git a/flit_core/flit_core/common.py b/flit_core/flit_core/common.py index f1f378f5..0d6716c7 100644 --- a/flit_core/flit_core/common.py +++ b/flit_core/flit_core/common.py @@ -163,10 +163,13 @@ def get_docstring_and_version_via_import(target): _import_i += 1 log.debug("Loading module %s", target.file) - from importlib.machinery import SourceFileLoader - sl = SourceFileLoader('flit_core.dummy.import%d' % _import_i, str(target.file)) + from importlib.util import spec_from_file_location, module_from_spec + mod_name = 'flit_core.dummy.import%d' % _import_i + spec = spec_from_file_location(mod_name, target.file) with _module_load_ctx(): - m = sl.load_module() + m = module_from_spec(spec) + spec.loader.exec_module(m) + docstring = m.__dict__.get('__doc__', None) version = m.__dict__.get('__version__', None) return docstring, version @@ -407,7 +410,7 @@ def normalize_dist_name(name: str, version: str) -> str: See https://packaging.python.org/specifications/binary-distribution-format/#escaping-and-unicode """ - normalized_name = re.sub(r'[-_.]+', '_', name, flags=re.UNICODE) + normalized_name = re.sub(r'[-_.]+', '_', name, flags=re.UNICODE).lower() assert check_version(version) == version assert '-' not in version, 'Normalized versions can’t have dashes' return '{}-{}'.format(normalized_name, version) @@ -416,3 +419,20 @@ def normalize_dist_name(name: str, version: str) -> str: def dist_info_name(distribution, version): """Get the correct name of the .dist-info folder""" return normalize_dist_name(distribution, version) + '.dist-info' + + +def walk_data_dir(data_directory): + """Iterate over the files in the given data directory. + + Yields paths prefixed with data_directory - caller may want to make them + relative to that. Excludes any __pycache__ subdirectories. + """ + if data_directory is None: + return + + for dirpath, dirs, files in os.walk(data_directory): + for file in sorted(files): + full_path = os.path.join(dirpath, file) + yield full_path + + dirs[:] = [d for d in sorted(dirs) if d != '__pycache__'] diff --git a/flit_core/flit_core/config.py b/flit_core/flit_core/config.py index b0b6ddbd..ade14b8d 100644 --- a/flit_core/flit_core/config.py +++ b/flit_core/flit_core/config.py @@ -120,7 +120,7 @@ def prep_toml_config(d, path): ) unknown_sections = set(dtool) - { - 'metadata', 'module', 'scripts', 'entrypoints', 'sdist' + 'metadata', 'module', 'scripts', 'entrypoints', 'sdist', 'external-data' } unknown_sections = [s for s in unknown_sections if not s.lower().startswith('x-')] if unknown_sections: @@ -142,6 +142,27 @@ def prep_toml_config(d, path): dtool['sdist'].get('exclude', []), 'exclude' ) + data_dir = dtool.get('external-data', {}).get('directory', None) + if data_dir is not None: + toml_key = "tool.flit.external-data.directory" + if not isinstance(data_dir, str): + raise ConfigError(f"{toml_key} must be a string") + + normp = osp.normpath(data_dir) + if osp.isabs(normp): + raise ConfigError(f"{toml_key} cannot be an absolute path") + if normp.startswith('..' + os.sep): + raise ConfigError( + f"{toml_key} cannot point outside the directory containing pyproject.toml" + ) + if normp == '.': + raise ConfigError( + f"{toml_key} cannot refer to the directory containing pyproject.toml" + ) + loaded_cfg.data_directory = path.parent / data_dir + if not loaded_cfg.data_directory.is_dir(): + raise ConfigError(f"{toml_key} must refer to a directory") + return loaded_cfg def flatten_entrypoints(ep): @@ -207,7 +228,7 @@ def _check_glob_patterns(pats, clude): raise ConfigError( '{} pattern {!r} is an absolute path'.format(clude, p) ) - if osp.normpath(p).startswith('..' + os.sep): + if normp.startswith('..' + os.sep): raise ConfigError( '{} pattern {!r} points out of the directory containing pyproject.toml' .format(clude, p) @@ -227,6 +248,7 @@ def __init__(self): self.sdist_include_patterns = [] self.sdist_exclude_patterns = [] self.dynamic_metadata = [] + self.data_directory = None def add_scripts(self, scripts_dict): if scripts_dict: diff --git a/flit_core/flit_core/sdist.py b/flit_core/flit_core/sdist.py index 80ccf25d..cb72ea1d 100644 --- a/flit_core/flit_core/sdist.py +++ b/flit_core/flit_core/sdist.py @@ -72,13 +72,14 @@ class SdistBuilder: which is what should normally be published to PyPI. """ def __init__(self, module, metadata, cfgdir, reqs_by_extra, entrypoints, - extra_files, include_patterns=(), exclude_patterns=()): + extra_files, data_directory, include_patterns=(), exclude_patterns=()): self.module = module self.metadata = metadata self.cfgdir = cfgdir self.reqs_by_extra = reqs_by_extra self.entrypoints = entrypoints self.extra_files = extra_files + self.data_directory = data_directory self.includes = FilePatterns(include_patterns, str(cfgdir)) self.excludes = FilePatterns(exclude_patterns, str(cfgdir)) @@ -93,8 +94,8 @@ def from_ini_path(cls, ini_path: Path): extra_files = [ini_path.name] + ini_info.referenced_files return cls( module, metadata, srcdir, ini_info.reqs_by_extra, - ini_info.entrypoints, extra_files, ini_info.sdist_include_patterns, - ini_info.sdist_exclude_patterns, + ini_info.entrypoints, extra_files, ini_info.data_directory, + ini_info.sdist_include_patterns, ini_info.sdist_exclude_patterns, ) def prep_entry_points(self): @@ -115,6 +116,8 @@ def select_files(self): cfgdir_s = str(self.cfgdir) return [ osp.relpath(p, cfgdir_s) for p in self.module.iter_files() + ] + [ + osp.relpath(p, cfgdir_s) for p in common.walk_data_dir(self.data_directory) ] + self.extra_files def apply_includes_excludes(self, files): diff --git a/flit_core/flit_core/tests/samples/with_data_dir/LICENSE b/flit_core/flit_core/tests/samples/with_data_dir/LICENSE new file mode 100644 index 00000000..7f5c1948 --- /dev/null +++ b/flit_core/flit_core/tests/samples/with_data_dir/LICENSE @@ -0,0 +1 @@ +This file should be added to wheels diff --git a/flit_core/flit_core/tests/samples/with_data_dir/README.rst b/flit_core/flit_core/tests/samples/with_data_dir/README.rst new file mode 100644 index 00000000..ef7b7c19 --- /dev/null +++ b/flit_core/flit_core/tests/samples/with_data_dir/README.rst @@ -0,0 +1 @@ +This contains a nön-ascii character diff --git a/flit_core/flit_core/tests/samples/with_data_dir/data/share/man/man1/foo.1 b/flit_core/flit_core/tests/samples/with_data_dir/data/share/man/man1/foo.1 new file mode 100644 index 00000000..c12128d8 --- /dev/null +++ b/flit_core/flit_core/tests/samples/with_data_dir/data/share/man/man1/foo.1 @@ -0,0 +1 @@ +Example data file diff --git a/flit_core/flit_core/tests/samples/with_data_dir/module1.py b/flit_core/flit_core/tests/samples/with_data_dir/module1.py new file mode 100644 index 00000000..87f0370d --- /dev/null +++ b/flit_core/flit_core/tests/samples/with_data_dir/module1.py @@ -0,0 +1,3 @@ +"""Example module""" + +__version__ = '0.1' diff --git a/flit_core/flit_core/tests/samples/with_data_dir/pyproject.toml b/flit_core/flit_core/tests/samples/with_data_dir/pyproject.toml new file mode 100644 index 00000000..84d165e3 --- /dev/null +++ b/flit_core/flit_core/tests/samples/with_data_dir/pyproject.toml @@ -0,0 +1,26 @@ +[build-system] +requires = ["flit_core >=3.2,<4"] +build-backend = "flit_core.buildapi" + +[project] +name = "module1" +authors = [ + {name = "Sir Röbin", email = "robin@camelot.uk"} +] +readme = "README.rst" +license = {file = "LICENSE"} +requires-python = ">=3.7" +dependencies = [ + "requests >= 2.18", + "docutils", +] +dynamic = [ + "version", + "description", +] + +[project.scripts] +foo = "module1:main" + +[tool.flit.external-data] +directory = "data" diff --git a/flit_core/flit_core/tests/test_sdist.py b/flit_core/flit_core/tests/test_sdist.py index ca29c4e9..a513758e 100644 --- a/flit_core/flit_core/tests/test_sdist.py +++ b/flit_core/flit_core/tests/test_sdist.py @@ -49,3 +49,12 @@ def test_include_exclude(): assert osp.join('doc', 'test.rst') in files assert osp.join('doc', 'test.txt') not in files assert osp.join('doc', 'subdir', 'test.txt') in files + + +def test_data_dir(): + builder = sdist.SdistBuilder.from_ini_path( + samples_dir / 'with_data_dir' / 'pyproject.toml' + ) + files = builder.apply_includes_excludes(builder.select_files()) + + assert osp.join('data', 'share', 'man', 'man1', 'foo.1') in files diff --git a/flit_core/flit_core/tests/test_wheel.py b/flit_core/flit_core/tests/test_wheel.py index 9bb38fb2..310f9c6c 100644 --- a/flit_core/flit_core/tests/test_wheel.py +++ b/flit_core/flit_core/tests/test_wheel.py @@ -3,7 +3,7 @@ from testpath import assert_isfile -from flit_core.wheel import make_wheel_in +from flit_core.wheel import make_wheel_in, main samples_dir = Path(__file__).parent / 'samples' @@ -29,3 +29,19 @@ def test_zero_timestamp(tmp_path, monkeypatch): # Minimum value for zip timestamps is 1980-1-1 with ZipFile(info.file, 'r') as zf: assert zf.getinfo('module1a.py').date_time == (1980, 1, 1, 0, 0, 0) + + +def test_main(tmp_path): + main(['--outdir', str(tmp_path), str(samples_dir / 'pep621')]) + wheels = list(tmp_path.glob('*.whl')) + assert len(wheels) == 1 + # Minimum value for zip timestamps is 1980-1-1 + with ZipFile(wheels[0], 'r') as zf: + assert 'module1a.py' in zf.namelist() + + +def test_data_dir(tmp_path): + info = make_wheel_in(samples_dir / 'with_data_dir' / 'pyproject.toml', tmp_path) + assert_isfile(info.file) + with ZipFile(info.file, 'r') as zf: + assert 'module1-0.1.data/data/share/man/man1/foo.1' in zf.namelist() diff --git a/flit_core/flit_core/wheel.py b/flit_core/flit_core/wheel.py index 8c87451e..08cb70ae 100644 --- a/flit_core/flit_core/wheel.py +++ b/flit_core/flit_core/wheel.py @@ -1,3 +1,4 @@ +import argparse from base64 import urlsafe_b64encode import contextlib from datetime import datetime @@ -8,6 +9,7 @@ import os.path as osp import stat import tempfile +from pathlib import Path from types import SimpleNamespace from typing import Optional import zipfile @@ -57,13 +59,16 @@ def zip_timestamp_from_env() -> Optional[tuple]: class WheelBuilder: - def __init__(self, directory, module, metadata, entrypoints, target_fp): + def __init__( + self, directory, module, metadata, entrypoints, target_fp, data_directory + ): """Build a wheel from a module/package """ self.directory = directory self.module = module self.metadata = metadata self.entrypoints = entrypoints + self.data_directory = data_directory self.records = [] self.source_time_stamp = zip_timestamp_from_env() @@ -74,14 +79,15 @@ def __init__(self, directory, module, metadata, entrypoints, target_fp): @classmethod def from_ini_path(cls, ini_path, target_fp): - # Local import so bootstrapping doesn't try to load toml from .config import read_flit_config directory = ini_path.parent ini_info = read_flit_config(ini_path) entrypoints = ini_info.entrypoints module = common.Module(ini_info.module, directory) metadata = common.make_metadata(module, ini_info) - return cls(directory, module, metadata, entrypoints, target_fp) + return cls( + directory, module, metadata, entrypoints, target_fp, ini_info.data_directory + ) @property def dist_info(self): @@ -160,6 +166,14 @@ def add_pth(self): with self._write_to_zip(self.module.name + ".pth") as f: f.write(str(self.module.source_dir.resolve())) + def add_data_directory(self): + dir_in_whl = '{}.data/data/'.format( + common.normalize_dist_name(self.metadata.name, self.metadata.version) + ) + for full_path in common.walk_data_dir(self.data_directory): + rel_path = os.path.relpath(full_path, self.data_directory) + self._add_file(full_path, dir_in_whl + rel_path) + def write_metadata(self): log.info('Writing metadata files') @@ -193,6 +207,7 @@ def build(self, editable=False): self.add_pth() else: self.copy_module() + self.add_data_directory() self.write_metadata() self.write_record() finally: @@ -215,3 +230,30 @@ def make_wheel_in(ini_path, wheel_directory, editable=False): log.info("Built wheel: %s", wheel_path) return SimpleNamespace(builder=wb, file=wheel_path) + + +def main(argv=None): + parser = argparse.ArgumentParser() + parser.add_argument( + 'srcdir', + type=Path, + nargs='?', + default=Path.cwd(), + help='source directory (defaults to current directory)', + ) + + parser.add_argument( + '--outdir', + '-o', + help='output directory (defaults to {srcdir}/dist)', + ) + args = parser.parse_args(argv) + outdir = args.srcdir / 'dist' if args.outdir is None else Path(args.outdir) + print("Building wheel from", args.srcdir) + pyproj_toml = args.srcdir / 'pyproject.toml' + outdir.mkdir(parents=True, exist_ok=True) + info = make_wheel_in(pyproj_toml, outdir) + print("Wheel built", outdir / info.file.name) + +if __name__ == "__main__": + main() diff --git a/flit_core/pyproject.toml b/flit_core/pyproject.toml index ca3f46b2..3652f9a3 100644 --- a/flit_core/pyproject.toml +++ b/flit_core/pyproject.toml @@ -19,3 +19,6 @@ dynamic = ["version"] [project.urls] Source = "https://github.com/pypa/flit" + +[tool.flit.sdist] +include = ["bootstrap_install.py", "build_dists.py"] diff --git a/pyproject.toml b/pyproject.toml index 64d14150..cb56b5c1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["flit_core >=3.6.0,<4"] +requires = ["flit_core >=3.7.0,<4"] build-backend = "flit_core.buildapi" [project] @@ -8,7 +8,7 @@ authors = [ {name = "Thomas Kluyver", email = "thomas@kluyver.me.uk"}, ] dependencies = [ - "flit_core >=3.6.0", + "flit_core >=3.7.0", "requests", "docutils", "tomli", diff --git a/tests/samples/altdistname/pyproject.toml b/tests/samples/altdistname/pyproject.toml index 00caf94c..c2d08cad 100644 --- a/tests/samples/altdistname/pyproject.toml +++ b/tests/samples/altdistname/pyproject.toml @@ -7,5 +7,5 @@ module = "package1" author = "Sir Robin" author-email = "robin@camelot.uk" home-page = "http://github.com/sirrobin/package1" -dist-name = "package-dist1" +dist-name = "package-Dist1" diff --git a/tests/test_install.py b/tests/test_install.py index 77a90076..b4e9068e 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -21,11 +21,13 @@ class InstallTests(TestCase): def setUp(self): td = tempfile.TemporaryDirectory() - scripts_dir = os.path.join(td.name, 'scripts') - purelib_dir = os.path.join(td.name, 'site-packages') self.addCleanup(td.cleanup) self.get_dirs_patch = patch('flit.install.get_dirs', - return_value={'scripts': scripts_dir, 'purelib': purelib_dir}) + return_value={ + 'scripts': os.path.join(td.name, 'scripts'), + 'purelib': os.path.join(td.name, 'site-packages'), + 'data': os.path.join(td.name, 'data'), + }) self.get_dirs_patch.start() self.tmpdir = pathlib.Path(td.name) @@ -167,6 +169,8 @@ def test_symlink_module_pep621(self): ) def test_symlink_module_in_src(self): + if os.name == 'nt': + raise SkipTest("symlink") oldcwd = os.getcwd() os.chdir(samples_dir / 'packageinsrc') try: @@ -244,11 +248,13 @@ def test_symlink_other_python(self): # Called by Installer._get_dirs() : script2 = ("#!{python}\n" "import json, sys\n" - "json.dump({{'purelib': {purelib!r}, 'scripts': {scripts!r} }}, " + "json.dump({{'purelib': {purelib!r}, 'scripts': {scripts!r}, 'data': {data!r} }}, " "sys.stdout)" ).format(python=sys.executable, purelib=str(self.tmpdir / 'site-packages2'), - scripts=str(self.tmpdir / 'scripts2')) + scripts=str(self.tmpdir / 'scripts2'), + data=str(self.tmpdir / 'data'), + ) with MockCommand('mock_python', content=script1): ins = Installer.from_ini_path(samples_dir / 'package1' / 'pyproject.toml', python='mock_python', @@ -286,6 +292,25 @@ def test_extras_error(self): Installer.from_ini_path(samples_dir / 'requires-requests.toml', user=False, deps='none', extras='dev') + def test_install_data_dir(self): + Installer.from_ini_path( + core_samples_dir / 'with_data_dir' / 'pyproject.toml', + ).install_directly() + assert_isfile(self.tmpdir / 'site-packages' / 'module1.py') + assert_isfile(self.tmpdir / 'data' / 'share' / 'man' / 'man1' / 'foo.1') + + def test_symlink_data_dir(self): + if os.name == 'nt': + raise SkipTest("symlink") + Installer.from_ini_path( + core_samples_dir / 'with_data_dir' / 'pyproject.toml', symlink=True + ).install_directly() + assert_isfile(self.tmpdir / 'site-packages' / 'module1.py') + assert_islink( + self.tmpdir / 'data' / 'share' / 'man' / 'man1' / 'foo.1', + to=core_samples_dir / 'with_data_dir' / 'data' / 'share' / 'man' / 'man1' / 'foo.1' + ) + @pytest.mark.parametrize(('deps', 'extras', 'installed'), [ ('none', [], set()), ('develop', [], {'pytest ;', 'toml ;'}), diff --git a/tox.ini b/tox.ini index 71bc8867..e1689d66 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{39,38,37,36},bootstrap +envlist = py{310,39,38,37,36},bootstrap skip_missing_interpreters = true [gh-actions] @@ -8,6 +8,7 @@ python = 3.7: py37 3.8: py38, bootstrap 3.9: py39 + 3.10: py310 [testenv] deps =