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 =