Skip to content
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

Add YAML configuration file support #137

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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: 2 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
unreleased
----------

- Add configuration file format choices, ini or yaml.

- Fix unwanted literal inclusion of a METAL attribute in rendered output within
the Chameleon ``layout.pt`` template.

Expand Down
2 changes: 2 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ A Cookiecutter (project template) for creating a Pyramid starter project.

Customizable options upon install include choice of:

* configuration file format (ini, YAML)
* template language (Jinja2, Chameleon, or Mako)
* persistent backend (none, SQLAlchemy with SQLite, or ZODB)
* mapping of URLs to routes (if the selected persistent backend is "none" or "sqlalchemy" then URL dispatch, or if "zodb" then traversal)
Expand Down Expand Up @@ -73,6 +74,7 @@ Usage
$ env/bin/pytest

#. Run your project.
(Change the "ini" suffix, if you chose a different configuration file format.)

.. code-block:: bash

Expand Down
1 change: 1 addition & 0 deletions cookiecutter.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"project_name": "Pyramid Scaffold",
"repo_name": "{{cookiecutter.project_name.lower().strip().replace(' ', '_').replace(':', '_').replace('-', '_').replace('!', '_')}}",
"configuration_file_type": ["ini", "yaml"],
"template_language": ["jinja2", "chameleon", "mako"],
"backend": ["none", "sqlalchemy", "zodb"],
"_copy_without_render": [
Expand Down
34 changes: 30 additions & 4 deletions hooks/post_gen_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,34 @@


def main():
tidy_config_files()
clean_unused_template_settings()
clean_unused_backend()
display_actions_message()


def tidy_config_files():
conf_basenames = ['development', 'production', 'testing']
conf_exts = ['ini', 'yaml'] # Generated extensions
selected_conf_ext = '{{ cookiecutter.configuration_file_type }}'

for some_file in conf_basenames:
for some_ext in conf_exts:
if some_ext != selected_conf_ext:
conf_path = os.path.join(WORKING, f'{some_file}.{some_ext}')
if (selected_conf_ext != 'ini'
and some_ext == 'ini'
and '{{ cookiecutter.backend }}' == 'sqlalchemy'):
# Only alembic uses the ini config files. Rename the
# standard config file to preface name with 'alembic_'.
os.rename(
conf_path,
os.path.join(
WORKING, f'alembic_{some_file}.{some_ext}'))
else:
os.unlink(conf_path) # remove unused config file


def clean_unused_template_settings():
selected_lang = '{{ cookiecutter.template_language }}'
templates = os.path.join(
Expand Down Expand Up @@ -88,6 +111,9 @@ def delete_other_files(directory, current_prefix, rm_prefixes):
delete_other_files(full_path, current_prefix, rm_prefixes)


{% set conf_prefix = (
'' if cookiecutter.configuration_file_type == 'ini'
else 'alembic_' ) -%}
def display_actions_message():
WIN = sys.platform.startswith('win')

Expand Down Expand Up @@ -137,19 +163,19 @@ def display_actions_message():
{% if cookiecutter.backend == 'sqlalchemy' -%}
Initialize and upgrade the database using Alembic.
# Generate your first revision.
%(alembic_cmd)s -c development.ini revision --autogenerate -m "init"
%(alembic_cmd)s -c {{ conf_prefix }}development.ini revision --autogenerate -m "init"
# Upgrade to that revision.
%(alembic_cmd)s -c development.ini upgrade head
%(alembic_cmd)s -c {{ conf_prefix }}development.ini upgrade head

Load default data into the database using a script.
%(init_cmd)s development.ini
%(init_cmd)s {{ conf_prefix }}development.ini

{% endif -%}
Run your project's tests.
%(pytest_cmd)s

Run your project.
%(pserve_cmd)s development.ini
%(pserve_cmd)s development.{{ cookiecutter.configuration_file_type }}
""" % env_setup)
print(msg)

Expand Down
110 changes: 74 additions & 36 deletions tests/test_it.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,18 @@
WIN = sys.platform == 'win32'
WORKING = os.path.abspath(os.path.join(os.path.curdir))

base_files = [
CONFIG_BASENAMES = [
'development',
'production',
'testing',
]

CONFIG_EXTS = [
'ini',
'yaml',
]

BASE_FILES = [
'.gitignore',
'/myapp/__init__.py',
'/myapp/routes.py',
Expand All @@ -26,13 +37,10 @@
'/tests/test_views.py',
'MANIFEST.in',
'README.md',
'development.ini',
'production.ini',
'pyproject.toml',
'testing.ini',
]

sqlalchemy_files = [
SQLALCHEMY_FILES = [
'.gitignore',
'/myapp/__init__.py',
'/myapp/alembic/env.py',
Expand Down Expand Up @@ -60,13 +68,10 @@
'/tests/test_views.py',
'MANIFEST.in',
'README.md',
'development.ini',
'production.ini',
'pyproject.toml',
'testing.ini',
]

zodb_files = [
ZODB_FILES = [
'.gitignore',
'/myapp/__init__.py',
'/myapp/models/__init__.py',
Expand All @@ -87,13 +92,22 @@
'/tests/test_views.py',
'MANIFEST.in',
'README.md',
'development.ini',
'production.ini',
'pyproject.toml',
'testing.ini',
]


def build_conf_files(config_type, backend):
"""Build a list of configuration file (relative) pathnames of the
configuration files the cookiecutter creates."""
config_paths = [f'{config_basename}.{config_type}'
for config_basename in CONFIG_BASENAMES]
if config_type != 'ini' and backend == 'sqlalchemy':
# alembic needs .ini config files
config_paths.extend([f'alembic_{config_basename}.ini'
for config_basename in CONFIG_BASENAMES])
return config_paths


def build_files_list(root_dir):
"""Build a list containing relative paths to the generated files."""
file_list = []
Expand All @@ -104,11 +118,13 @@ def build_files_list(root_dir):
return file_list


@pytest.mark.parametrize('config_type', CONFIG_EXTS)
@pytest.mark.parametrize('template', ['jinja2', 'mako', 'chameleon'])
def test_base(cookies, venv, capfd, template):
def test_base(cookies, venv, capfd, template, config_type):
result = cookies.bake(extra_context={
'project_name': 'Test Project',
'template_language': template,
'configuration_file_type': config_type,
'backend': 'none',
'repo_name': 'myapp',
})
Expand All @@ -117,15 +133,18 @@ def test_base(cookies, venv, capfd, template):

out, err = capfd.readouterr()

expected_files = BASE_FILES.copy()
expected_files.extend(build_conf_files(config_type, 'none'))

if WIN:
assert 'Scripts\\pserve' in out
for idx, base_file in enumerate(base_files):
base_files[idx] = base_file.replace('/', '\\')
base_files.sort()

for idx, base_file in enumerate(expected_files):
expected_files[idx] = base_file.replace('/', '\\')
else:
assert 'bin/pserve' in out

expected_files.sort()

# Get the file list generated by cookiecutter. Differs based on backend.
files = build_files_list(str(result.project_path))
files.sort()
Expand All @@ -134,11 +153,12 @@ def test_base(cookies, venv, capfd, template):
if template == 'chameleon':
template = 'pt'

for idx, base_file in enumerate(base_files):
for idx, base_file in enumerate(expected_files):
if 'templates' in base_file:
base_files[idx] = base_files[idx].split('.')[0] + '.' + template
expected_files[idx] = expected_files[idx].split('.')[
0] + '.' + template

assert base_files == files
assert expected_files == files

cwd = str(result.project_path)

Expand All @@ -151,11 +171,13 @@ def test_base(cookies, venv, capfd, template):
subprocess.check_call([venv.python, '-m', 'pytest', '-q'], cwd=cwd)


@pytest.mark.parametrize('config_type', CONFIG_EXTS)
@pytest.mark.parametrize('template', ['jinja2', 'mako', 'chameleon'])
def test_zodb(cookies, venv, capfd, template):
def test_zodb(cookies, venv, capfd, template, config_type):
result = cookies.bake(extra_context={
'project_name': 'Test Project',
'template_language': template,
'configuration_file_type': config_type,
'backend': 'zodb',
'repo_name': 'myapp',
})
Expand All @@ -164,14 +186,18 @@ def test_zodb(cookies, venv, capfd, template):

out, err = capfd.readouterr()

expected_files = ZODB_FILES.copy()
expected_files.extend(build_conf_files(config_type, 'zodb'))

if WIN:
assert 'Scripts\\pserve' in out
for idx, zodb_file in enumerate(zodb_files):
zodb_files[idx] = zodb_file.replace('/', '\\')
zodb_files.sort()
for idx, zodb_file in enumerate(expected_files):
expected_files[idx] = zodb_file.replace('/', '\\')
else:
assert 'bin/pserve' in out

expected_files.sort()

# Get the file list generated by cookiecutter. Differs based on backend.
files = build_files_list(str(result.project_path))
files.sort()
Expand All @@ -180,11 +206,12 @@ def test_zodb(cookies, venv, capfd, template):
if template == 'chameleon':
template = 'pt'

for idx, zodb_file in enumerate(zodb_files):
for idx, zodb_file in enumerate(expected_files):
if 'templates' in zodb_file:
zodb_files[idx] = zodb_files[idx].split('.')[0] + '.' + template
expected_files[idx] = expected_files[idx].split('.')[
0] + '.' + template

assert zodb_files == files
assert expected_files == files

cwd = str(result.project_path)

Expand All @@ -197,11 +224,13 @@ def test_zodb(cookies, venv, capfd, template):
subprocess.check_call([venv.python, '-m', 'pytest', '-q'], cwd=cwd)


@pytest.mark.parametrize('config_type', CONFIG_EXTS)
@pytest.mark.parametrize('template', ['jinja2', 'mako', 'chameleon'])
def test_sqlalchemy(cookies, venv, capfd, template):
def test_sqlalchemy(cookies, venv, capfd, template, config_type):
result = cookies.bake(extra_context={
'project_name': 'Test Project',
'template_language': template,
'configuration_file_type': config_type,
'backend': 'sqlalchemy',
'repo_name': 'myapp',
})
Expand All @@ -210,14 +239,18 @@ def test_sqlalchemy(cookies, venv, capfd, template):

out, err = capfd.readouterr()

expected_files = SQLALCHEMY_FILES.copy()
expected_files.extend(build_conf_files(config_type, 'sqlalchemy'))

if WIN:
assert 'Scripts\\pserve' in out
for idx, sqlalchemy_file in enumerate(sqlalchemy_files):
sqlalchemy_files[idx] = sqlalchemy_file.replace('/', '\\')
sqlalchemy_files.sort()
for idx, sqlalchemy_file in enumerate(expected_files):
expected_files[idx] = sqlalchemy_file.replace('/', '\\')
else:
assert 'bin/pserve' in out

expected_files.sort()

# Get the file list generated by cookiecutter. Differs based on backend.
files = build_files_list(str(result.project_path))
files.sort()
Expand All @@ -226,12 +259,12 @@ def test_sqlalchemy(cookies, venv, capfd, template):
if template == 'chameleon':
template = 'pt'

for idx, sqlalchemy_file in enumerate(sqlalchemy_files):
for idx, sqlalchemy_file in enumerate(expected_files):
if 'templates' in sqlalchemy_file:
sqlalchemy_files[idx] = sqlalchemy_files[idx].split('.')[
expected_files[idx] = expected_files[idx].split('.')[
0] + '.' + template

assert sqlalchemy_files == files
assert expected_files == files

cwd = str(result.project_path)

Expand All @@ -241,12 +274,17 @@ def test_sqlalchemy(cookies, venv, capfd, template):
venv.install(os.environ['OVERRIDE_PYRAMID'], editable=True)

venv.install(cwd + '[testing]', editable=True)

if config_type == 'ini':
alembic_cfg_file = 'testing.ini'
else:
alembic_cfg_file = 'alembic_testing.ini'
create_migration_script = textwrap.dedent(
'''
f'''
import alembic.config
import alembic.command

config = alembic.config.Config('testing.ini')
config = alembic.config.Config('{alembic_cfg_file}')
alembic.command.revision(
config,
autogenerate=True,
Expand Down
11 changes: 7 additions & 4 deletions {{cookiecutter.repo_name}}/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,27 @@
```

{% if cookiecutter.backend == 'sqlalchemy' -%}
{% set conf_prefix = (
'' if cookiecutter.configuration_file_type == 'ini'
else 'alembic_' ) -%}
- Initialize and upgrade the database using Alembic.

- Generate your first revision.

```
env/bin/alembic -c development.ini revision --autogenerate -m "init"
env/bin/alembic -c {{ conf_prefix }}development.ini revision --autogenerate -m "init"
```

- Upgrade to that revision.

```
env/bin/alembic -c development.ini upgrade head
env/bin/alembic -c {{ conf_prefix }}development.ini upgrade head
```

- Load default data into the database using a script.

```
env/bin/initialize_{{ cookiecutter.repo_name }}_db development.ini
env/bin/initialize_{{ cookiecutter.repo_name }}_db {{ conf_prefix }}development.ini
```

{% endif -%}
Expand All @@ -58,5 +61,5 @@
- Run your project.

```
env/bin/pserve development.ini
env/bin/pserve development.{{ cookiecutter.configuration_file_type }}
```
Loading