Skip to content

Commit

Permalink
Merge pull request #301 from takluyver/new-bootstrap-dev
Browse files Browse the repository at this point in the history
Development install for Flit itself
  • Loading branch information
takluyver committed Dec 17, 2019
2 parents 46173cb + d6313e6 commit 2740492
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 36 deletions.
15 changes: 0 additions & 15 deletions README.rst
Expand Up @@ -73,18 +73,3 @@ To install a package locally for development, run::
Flit packages a single importable module or package at a time, using the import
name as the name on PyPI. All subpackages and data files within a package are
included automatically.

Development
-----------

To install the development version of Flit from Github::

git clone https://github.com/takluyver/flit.git
cd flit
python3 -m pip install docutils requests pytoml
python3 -m flit install

You may want to use the ``--symlink`` or ``--pth-file`` options so you can test
changes without reinstalling it.

To run the tests, run ``pytest``.
48 changes: 48 additions & 0 deletions bootstrap_dev.py
@@ -0,0 +1,48 @@
#!/usr/bin/env python3

# Symlink install flit & flit_core for development.
# Most projects can do the same with 'flit install --symlink'.
# But that doesn't work until Flit is installed, so we need some bootstrapping.

import argparse
import logging
import os
from pathlib import Path
import sys

my_dir = Path(__file__).parent
os.chdir(my_dir)
sys.path.insert(0, 'flit_core')

from flit_core import build_thyself
from flit_core.inifile import LoadedConfig
from flit.install import Installer

ap = argparse.ArgumentParser()
ap.add_argument('--user')
args = ap.parse_args()

logging.basicConfig(level=logging.INFO)

# Construct config for flit_core
core_config = LoadedConfig()
core_config.module = 'flit_core'
core_config.metadata = build_thyself.metadata_dict
core_config.reqs_by_extra['.none'] = build_thyself.metadata.requires_dist

install_kwargs = {'symlink': True}
if os.name == 'nt':
# Use .pth files instead of symlinking on Windows
install_kwargs = {'symlink': False, 'pth': True}

# Install flit_core
Installer(
my_dir / 'flit_core', core_config, user=args.user, **install_kwargs
).install()
print("Linked flit_core into site-packages.")

# Install flit
Installer.from_ini_path(
my_dir / 'pyproject.toml', user=args.user, **install_kwargs
).install()
print("Linked flit into site-packages.")
26 changes: 26 additions & 0 deletions doc/development.rst
@@ -0,0 +1,26 @@
Developing Flit
===============

To get a development installation of Flit itself::

git clone https://github.com/takluyver/flit.git
cd flit
python3 -m pip install docutils requests pytoml
python3 bootstrap_dev.py

This links Flit into the current Python environment, so you can make changes
and try them without having to reinstall each time.

Testing
-------

To run the tests in separate environments for each available Python version::

tox

`tox <https://tox.readthedocs.io/en/latest/>`_ has many options.

To run the tests in your current environment, run::

pytest

5 changes: 5 additions & 0 deletions doc/index.rst
Expand Up @@ -17,6 +17,11 @@ Documentation contents
cmdline
upload
reproducible

.. toctree::
:maxdepth: 1

development
history

Indices and tables
Expand Down
2 changes: 1 addition & 1 deletion flit/__init__.py
Expand Up @@ -150,7 +150,7 @@ def main(argv=None):
from .install import Installer
try:
python = find_python_executable(args.python)
Installer(args.ini_file, user=args.user, python=python,
Installer.from_ini_path(args.ini_file, user=args.user, python=python,
symlink=args.symlink, deps=args.deps, extras=args.extras,
pth=args.pth_file).install()
except (ConfigError, PythonNotFoundError, common.NoDocstringError, common.NoVersionError) as e:
Expand Down
25 changes: 19 additions & 6 deletions flit/install.py
Expand Up @@ -89,9 +89,10 @@ def __str__(self):
return 'To install dependencies for extras, you cannot set deps=none.'

class Installer(object):
def __init__(self, ini_path, user=None, python=sys.executable,
def __init__(self, directory, ini_info, user=None, python=sys.executable,
symlink=False, deps='all', extras=(), pth=False):
self.ini_path = ini_path
self.directory = directory
self.ini_info = ini_info
self.python = python
self.symlink = symlink
self.pth = pth
Expand All @@ -103,8 +104,7 @@ def __init__(self, ini_path, user=None, python=sys.executable,
if deps == 'none' and extras:
raise DependencyError()

self.ini_info = inifile.read_flit_config(ini_path)
self.module = common.Module(self.ini_info.module, str(ini_path.parent))
self.module = common.Module(self.ini_info.module, str(directory))

if (hasattr(os, 'getuid') and (os.getuid() == 0) and
(not os.environ.get('FLIT_ROOT_INSTALL'))):
Expand All @@ -118,6 +118,13 @@ def __init__(self, ini_path, user=None, python=sys.executable,

self.installed_files = []

@classmethod
def from_ini_path(cls, ini_path, user=None, python=sys.executable,
symlink=False, deps='all', extras=(), pth=False):
ini_info = inifile.read_flit_config(ini_path)
return cls(ini_path.parent, ini_info, user=user, python=python,
symlink=symlink, deps=deps, extras=extras, pth=pth)

def _run_python(self, code=None, file=None, extra_args=()):
if code and file:
raise ValueError('Specify code or file, not both')
Expand Down Expand Up @@ -258,7 +265,7 @@ def install_reqs_my_python_if_needed(self):

log.warning("Installing requirements to Flit's env to import module.")
user = self.user if (self.python == sys.executable) else None
i2 = Installer(ini_path=self.ini_path, user=user, deps='production')
i2 = Installer(self.directory, self.ini_info, user=user, deps='production')
i2.install_requirements()

def _get_dirs(self, user):
Expand Down Expand Up @@ -326,7 +333,13 @@ def install_with_pip(self):
with tempfile.TemporaryDirectory() as td:
temp_whl = osp.join(td, 'temp.whl')
with open(temp_whl, 'w+b') as fp:
wb = WheelBuilder.from_ini_path(self.ini_path, fp)
wb = WheelBuilder(
str(self.directory),
self.module,
metadata=common.make_metadata(self.module, self.ini_info),
entrypoints=self.ini_info.entrypoints,
target_fp=fp,
)
wb.build()

renamed_whl = osp.join(td, wb.wheel_filename)
Expand Down
5 changes: 3 additions & 2 deletions flit_core/flit_core/build_thyself.py
Expand Up @@ -15,7 +15,7 @@

from . import __version__

metadata = Metadata({
metadata_dict = {
'name': 'flit_core',
'version': __version__,
'author': 'Thomas Kluyver & contributors',
Expand All @@ -31,7 +31,8 @@
"License :: OSI Approved :: BSD License",
"Topic :: Software Development :: Libraries :: Python Modules",
]
})
}
metadata = Metadata(metadata_dict)

def get_requires_for_build_wheel(config_settings=None):
"""Returns a list of requirements for building, as strings"""
Expand Down
22 changes: 11 additions & 11 deletions tests/test_install.py
Expand Up @@ -28,12 +28,12 @@ def tearDown(self):
self.get_dirs_patch.stop()

def test_install_module(self):
Installer(samples_dir / 'module1' / 'flit.ini').install_directly()
Installer.from_ini_path(samples_dir / 'module1' / 'flit.ini').install_directly()
assert_isfile(self.tmpdir / 'site-packages' / 'module1.py')
assert_isdir(self.tmpdir / 'site-packages' / 'module1-0.1.dist-info')

def test_install_package(self):
Installer(samples_dir / 'package1' / 'flit.ini').install_directly()
Installer.from_ini_path(samples_dir / 'package1' / 'flit.ini').install_directly()
assert_isdir(self.tmpdir / 'site-packages' / 'package1')
assert_isdir(self.tmpdir / 'site-packages' / 'package1-0.1.dist-info')
assert_isfile(self.tmpdir / 'scripts' / 'pkg_script')
Expand All @@ -43,31 +43,31 @@ def test_install_package(self):
def test_symlink_package(self):
if os.name == 'nt':
raise SkipTest("symlink")
Installer(samples_dir / 'package1' / 'flit.ini', symlink=True).install()
Installer.from_ini_path(samples_dir / 'package1' / 'flit.ini', symlink=True).install()
assert_islink(self.tmpdir / 'site-packages' / 'package1',
to=samples_dir / 'package1' / 'package1')
assert_isfile(self.tmpdir / 'scripts' / 'pkg_script')
with (self.tmpdir / 'scripts' / 'pkg_script').open() as f:
assert f.readline().strip() == "#!" + sys.executable

def test_pth_package(self):
Installer(samples_dir / 'package1' / 'flit.ini', pth=True).install()
Installer.from_ini_path(samples_dir / 'package1' / 'flit.ini', pth=True).install()
assert_isfile(self.tmpdir / 'site-packages' / 'package1.pth')
with open(str(self.tmpdir / 'site-packages' / 'package1.pth')) as f:
assert f.read() == str(samples_dir / 'package1')
assert_isfile(self.tmpdir / 'scripts' / 'pkg_script')

def test_dist_name(self):
Installer(samples_dir / 'altdistname' / 'flit.ini').install_directly()
Installer.from_ini_path(samples_dir / 'altdistname' / 'flit.ini').install_directly()
assert_isdir(self.tmpdir / 'site-packages' / 'package1')
assert_isdir(self.tmpdir / 'site-packages' / 'package_dist1-0.1.dist-info')

def test_entry_points(self):
Installer(samples_dir / 'entrypoints_valid' / 'flit.ini').install_directly()
Installer.from_ini_path(samples_dir / 'entrypoints_valid' / 'flit.ini').install_directly()
assert_isfile(self.tmpdir / 'site-packages' / 'package1-0.1.dist-info' / 'entry_points.txt')

def test_pip_install(self):
ins = Installer(samples_dir / 'package1' / 'flit.ini', python='mock_python',
ins = Installer.from_ini_path(samples_dir / 'package1' / 'flit.ini', python='mock_python',
user=False)

with MockCommand('mock_python') as mock_py:
Expand Down Expand Up @@ -103,7 +103,7 @@ def test_symlink_other_python(self):
scripts=str(self.tmpdir / 'scripts2'))

with MockCommand('mock_python', content=script1):
ins = Installer(samples_dir / 'package1' / 'flit.ini', python='mock_python',
ins = Installer.from_ini_path(samples_dir / 'package1' / 'flit.ini', python='mock_python',
symlink=True)
with MockCommand('mock_python', content=script2):
ins.install()
Expand All @@ -115,7 +115,7 @@ def test_symlink_other_python(self):
assert f.readline().strip() == "#!mock_python"

def test_install_requires(self):
ins = Installer(samples_dir / 'requires-requests.toml',
ins = Installer.from_ini_path(samples_dir / 'requires-requests.toml',
user=False, python='mock_python')

with MockCommand('mock_python') as mockpy:
Expand All @@ -126,7 +126,7 @@ def test_install_requires(self):

def test_extras_error(self):
with pytest.raises(DependencyError):
Installer(samples_dir / 'requires-requests.toml',
Installer.from_ini_path(samples_dir / 'requires-requests.toml',
user=False, deps='none', extras='dev')

@pytest.mark.parametrize(('deps', 'extras', 'installed'), [
Expand All @@ -139,7 +139,7 @@ def test_install_requires_extra(deps, extras, installed):
it = InstallTests()
try:
it.setUp()
ins = Installer(samples_dir / 'extras' / 'pyproject.toml', python='mock_python',
ins = Installer.from_ini_path(samples_dir / 'extras' / 'pyproject.toml', python='mock_python',
user=False, deps=deps, extras=extras)

cmd = MockCommand('mock_python')
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
@@ -1,5 +1,5 @@
[tox]
envlist = py{38,37,36,35,34,27}
envlist = py{38,37,36,35,34,27},bootstrap
skip_missing_interpreters = true

[testenv]
Expand Down

0 comments on commit 2740492

Please sign in to comment.