Skip to content

Projectfile #39

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Sep 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b353e58
Initial implementation of pyproject file.
amjith Sep 21, 2017
4c884ad
Make init interactive.
amjith Sep 23, 2017
974b020
Make new interactive.
amjith Sep 23, 2017
3d84279
Guess username and email from git config.
amjith Sep 24, 2017
4b8ac22
Add -i option to make init command interactive.
amjith Sep 24, 2017
4b35b6e
Add -i option to make new command interactive.
amjith Sep 24, 2017
2e931da
Handle the supplied license.
amjith Sep 24, 2017
4c7e0e9
Don't force package_name into settings.
amjith Sep 24, 2017
dff25d9
Fix failing setup tests.
amjith Sep 24, 2017
5cedca5
Use the version number in __init__.py creation.
amjith Sep 25, 2017
dd61c90
Add tests for interactive init and fix various bugs.
amjith Sep 25, 2017
b550bbf
Fix failing setup tests.
amjith Sep 25, 2017
3290028
Add toml as a dependency for testing.
amjith Sep 25, 2017
e391cc4
Fixing tests.
amjith Sep 25, 2017
eb275fc
Update appveyor dependency.
amjith Sep 25, 2017
2d8af52
Add tests for interactive new.
amjith Sep 25, 2017
3d3e66b
Merge branch 'master' into projectfile
amjith Sep 25, 2017
7cae8d6
Fix the PR comments.
amjith Sep 26, 2017
c9a5b75
Fix the license tests.
amjith Sep 26, 2017
45e4050
Change projectfile format.
amjith Sep 26, 2017
0250257
Use read_file in tests.
amjith Sep 27, 2017
52f08af
Add build-system section to pyproject.toml
amjith Sep 27, 2017
af8e6e1
Replace [[source]] with [metadata] in pyproject.
amjith Sep 27, 2017
0b8bec6
Match new with init.
amjith Sep 27, 2017
37af406
Fix tests to look for metadata instead of source.
amjith Sep 27, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ init:
- set PATH=%PYTHON%;%PYTHON%\Scripts;%PATH%

install:
- "%PYTHON%\\python.exe -m pip install pytest parse"
- "%PYTHON%\\python.exe -m pip install pytest parse toml"
- "%PYTHON%\\python.exe -m pip install -e ."

build: off
Expand Down
39 changes: 28 additions & 11 deletions hatch/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
)
from hatch.create import create_package
from hatch.env import install_packages
from hatch.settings import load_settings
from hatch.settings import load_settings, copy_default_settings
from hatch.venv import VENV_DIR, create_venv, venv
from hatch.utils import basepath


@click.command(context_settings=CONTEXT_SETTINGS,
short_help='Creates a new Python project in the current directory')
@click.argument('name')
@click.argument('name', required=False)
@click.option('-ne', '--no-env', is_flag=True,
help=(
'Disables the creation of a dedicated virtual env.'
Expand Down Expand Up @@ -47,7 +48,9 @@
))
@click.option('-l', '--licenses',
help='Comma-separated list of licenses to use.')
def init(name, no_env, pyname, pypath, global_packages, env_name, basic, cli, licenses):
@click.option('-i', '--interactive', is_flag=True, help=('Invoke interactive mode.'))
def init(name, no_env, pyname, pypath, global_packages, env_name, basic, cli,
licenses, interactive):
"""Creates a new Python project in the current directory.

Values from your config file such as `name` and `pyversions` will be used
Expand Down Expand Up @@ -83,23 +86,37 @@ def init(name, no_env, pyname, pypath, global_packages, env_name, basic, cli, li
try:
settings = load_settings()
except FileNotFoundError:
settings = {}
settings = copy_default_settings()
echo_warning(
'Unable to locate config file; try `hatch config --restore`. '
'The default project structure will be used.'
)

if basic:
settings['basic'] = True
cwd = os.getcwd()
if name:
package_name = name

if interactive or not name:
pname = basepath(cwd)
package_name = click.prompt('project name', default=(name or pname))
settings['version'] = click.prompt('version', default='1.0.0')
settings['description'] = click.prompt('description', default='')
settings['name'] = click.prompt('author',
default=settings.get('name') or '')
settings['email'] = click.prompt('author_email',
default=settings.get('email') or '')
licenses = click.prompt('license', default=(licenses or 'mit'))

if licenses:
settings['licenses'] = licenses.split(',')
settings['licenses'] = map(str.strip, licenses.split(','))

if basic:
settings['basic'] = True

settings['cli'] = cli

d = os.getcwd()
create_package(d, name, settings)
echo_success('Created project `{}` here'.format(name))
create_package(cwd, package_name, settings)
echo_success('Created project `{}` here'.format(package_name))

venvs = env_name.split('/') if env_name else []
if (venvs or not no_env) and pyname:
Expand All @@ -115,7 +132,7 @@ def init(name, no_env, pyname, pypath, global_packages, env_name, basic, cli, li
sys.exit(1)

if not no_env:
venv_dir = os.path.join(d, 'venv')
venv_dir = os.path.join(cwd, 'venv')
echo_waiting('Creating its own virtual env... ', nl=False)
create_venv(venv_dir, pypath=pypath, use_global=global_packages)
echo_success('complete!')
Expand Down
33 changes: 23 additions & 10 deletions hatch/commands/new.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
)
from hatch.create import create_package
from hatch.env import install_packages
from hatch.settings import load_settings
from hatch.settings import load_settings, copy_default_settings
from hatch.utils import chdir
from hatch.venv import VENV_DIR, create_venv, venv


@click.command(context_settings=CONTEXT_SETTINGS,
short_help='Creates a new Python project')
@click.argument('name')
@click.argument('name', required=False)
@click.option('-ne', '--no-env', is_flag=True,
help=(
'Disables the creation of a dedicated virtual env.'
Expand Down Expand Up @@ -48,7 +48,9 @@
))
@click.option('-l', '--licenses',
help='Comma-separated list of licenses to use.')
def new(name, no_env, pyname, pypath, global_packages, env_name, basic, cli, licenses):
@click.option('-i', '--interactive', is_flag=True, help=('Invoke interactive mode.'))
def new(name, no_env, pyname, pypath, global_packages, env_name, basic, cli,
licenses, interactive):
"""Creates a new Python project.

Values from your config file such as `name` and `pyversions` will be used
Expand Down Expand Up @@ -84,7 +86,7 @@ def new(name, no_env, pyname, pypath, global_packages, env_name, basic, cli, lic
try:
settings = load_settings()
except FileNotFoundError:
settings = {}
settings = copy_default_settings()
echo_warning(
'Unable to locate config file; try `hatch config --restore`. '
'The default project structure will be used.'
Expand All @@ -93,18 +95,29 @@ def new(name, no_env, pyname, pypath, global_packages, env_name, basic, cli, lic
if basic:
settings['basic'] = True

if licenses:
settings['licenses'] = licenses.split(',')

settings['cli'] = cli

origin = os.getcwd()
d = os.path.join(origin, name)
package_name = name if name else click.prompt('project name')

d = os.path.join(origin, package_name)

if os.path.exists(d):
echo_failure('Directory `{}` already exists.'.format(d))
sys.exit(1)

if interactive or not name:
settings['version'] = click.prompt('version', default='1.0.0')
settings['description'] = click.prompt('description', default='')
settings['name'] = click.prompt('author',
default=settings.get('name') or '')
settings['email'] = click.prompt('author_email',
default=settings.get('email') or '')
licenses = click.prompt('license', default=(licenses or 'mit'))

if licenses:
settings['licenses'] = map(str.strip, licenses.split(','))

venvs = env_name.split('/') if env_name else []
if (venvs or not no_env) and pyname:
try:
Expand All @@ -120,8 +133,8 @@ def new(name, no_env, pyname, pypath, global_packages, env_name, basic, cli, lic

os.makedirs(d)
with chdir(d, cwd=origin):
create_package(d, name, settings)
echo_success('Created project `{}`'.format(name))
create_package(d, package_name, settings)
echo_success('Created project `{}`'.format(package_name))

if not no_env:
venv_dir = os.path.join(d, 'venv')
Expand Down
14 changes: 10 additions & 4 deletions hatch/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
)
from hatch.files.readme import MarkdownReadme, ReStructuredTextReadme
from hatch.files.setup import SetupFile
from hatch.files.pyproject import ProjectFile
from hatch.files.vc import setup_git
from hatch.settings import DEFAULT_SETTINGS
from hatch.structures import Badge, File
Expand Down Expand Up @@ -40,8 +41,10 @@ def create_package(d, package_name, settings):
basic = settings.get('basic', DEFAULT_SETTINGS['basic'])
extra_files = []

name = settings.get('name') or DEFAULT_SETTINGS['name']
author = settings.get('name') or DEFAULT_SETTINGS['name']
version = settings.get('version') or '0.0.1'
email = settings.get('email') or DEFAULT_SETTINGS['email']
description = settings.get('description') or ''
pyversions = sorted(
settings.get('pyversions') or DEFAULT_SETTINGS['pyversions']
)
Expand All @@ -55,7 +58,7 @@ def create_package(d, package_name, settings):
)

licenses = [
LICENSES[li](name)
LICENSES[li](author)
for li in settings.get('licenses') or DEFAULT_SETTINGS['licenses']
]

Expand All @@ -80,9 +83,11 @@ def create_package(d, package_name, settings):
)

setup_py = SetupFile(
name, email, package_name, pyversions, licenses,
author, email, package_name, pyversions, licenses,
readme, package_url, cli
)
projectfile = ProjectFile(package_name, version, author, email,
description, pyversions, licenses, package_url)

coverage_service = settings.get('coverage') if not basic else None
if coverage_service:
Expand All @@ -99,7 +104,7 @@ def create_package(d, package_name, settings):
package_dir = os.path.join(d, normalized_package_name)
init_py = File(
'__init__.py',
"__version__ = '0.0.1'\n"
"__version__ = '{version}'\n".format(version=version)
)
init_py.write(package_dir)
create_file(os.path.join(d, 'tests', '__init__.py'))
Expand All @@ -119,6 +124,7 @@ def create_package(d, package_name, settings):
main_py.write(package_dir)

setup_py.write(d)
projectfile.write(d)
readme.write(d)
coveragerc.write(d)
tox.write(d)
Expand Down
39 changes: 39 additions & 0 deletions hatch/files/pyproject.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from hatch.structures import File

TEMPLATE = """\
[metadata]
name = '{name}'
version = '{version}'
description = '{description}'
author = '{author}'
author_email = '{author_email}'
license = '{license}'
url = '{url}'

[requires]
python_version = {python_version}

[build-system]
requires = ["setuptools", "wheel"]

[tool.hatch.commands]
prerelease = 'hatch build'
"""


class ProjectFile(File):
def __init__(self, name, version, author, email, description, pyversions,
licenses, package_url):
super(ProjectFile, self).__init__(
'pyproject.toml',
TEMPLATE.format(
name=name,
version=version,
author=author,
author_email=email,
description=description,
url=package_url,
license='/'.join(li.short_name for li in licenses),
python_version=pyversions,
)
)
16 changes: 16 additions & 0 deletions hatch/files/vc/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,19 @@ def setup_git(d, package_name):
print('Could not find `git` executable')
GitAttributes().write(d)
GitIgnore(package_name).write(d)

def get_user(): # no cov
try:
user = subprocess.check_output(['git', 'config', '--get', 'user.name'],
shell=NEED_SUBPROCESS_SHELL)
return user.strip().decode('utf-8')
except:
return

def get_email(): # no cov
try:
email = subprocess.check_output(['git', 'config', '--get', 'user.email'],
shell=NEED_SUBPROCESS_SHELL)
return email.strip().decode('utf-8')
except:
return
5 changes: 3 additions & 2 deletions hatch/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from atomicwrites import atomic_write

from hatch.utils import create_file, ensure_dir_exists
from hatch.files.vc.git import get_user, get_email

SETTINGS_FILE = os.path.join(user_data_dir('hatch', ''), 'settings.json')

Expand All @@ -19,8 +20,8 @@
('build', ''),
])),
('pypi_username', ''),
('name', 'U.N. Owen'),
('email', '[email protected]'),
('name', get_user() or 'U.N. Owen'),
('email', get_email() or '[email protected]'),
('basic', True),
('pyversions', ['2.7', '3.5', '3.6', 'pypy', 'pypy3']),
('licenses', ['mit', 'apache2']),
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
'wheel>=0.27.0'
),
setup_requires=('appdirs', 'atomicwrites'),
tests_require=('parse', ),
tests_require=('parse', 'toml'),

packages=find_packages(),
entry_points={
Expand Down
30 changes: 29 additions & 1 deletion tests/commands/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import sys

from click.testing import CliRunner
import toml

from hatch.cli import hatch
from hatch.env import get_editable_packages, get_python_version
Expand All @@ -10,7 +11,7 @@
)
from hatch.utils import create_file, remove_path, temp_chdir, temp_move_path
from hatch.venv import VENV_DIR, create_venv, get_new_venv_name, is_venv, venv
from ..utils import matching_file, wait_until
from ..utils import matching_file, wait_until, read_file


def test_config_not_exist():
Expand All @@ -27,6 +28,33 @@ def test_config_not_exist():
) in result.output
assert 'Created project `ok`' in result.output

def test_missing_name_invokes_interactive_mode():
with temp_chdir() as d:
runner = CliRunner()
runner.invoke(hatch, ['init', '--basic', '-ne'], input='ok')

assert os.path.exists(os.path.join(d, 'ok', '__init__.py'))


def test_interactive_mode():
with temp_chdir() as d:
runner = CliRunner()
runner.invoke(hatch, ['init', '-i', '--basic', '-ne'], input='ok\n0.1.0\nTest Description\nPicard\[email protected]\nmpl\n')

assert os.path.exists(os.path.join(d, 'ok', '__init__.py'))
assert "__version__ = '0.1.0'\n" == read_file(os.path.join(d, 'ok', '__init__.py'))
assert os.path.exists(os.path.join(d, 'LICENSE-MPL'))
assert os.path.exists(os.path.join(d, 'pyproject.toml'))
pyproject = toml.load('pyproject.toml')
metadata = pyproject['metadata']
assert metadata['name'] == 'ok'
assert metadata['version'] == '0.1.0'
assert metadata['description'] == 'Test Description'
assert metadata['author'] == 'Picard'
assert metadata['author_email'] == '[email protected]'
assert metadata['license'] == 'MPL-2.0'
assert metadata['url'] == 'https://github.com/_/ok'


def test_invalid_name():
with temp_chdir() as d:
Expand Down
Loading