Skip to content

Commit 39e32e3

Browse files
committed
[NEW] v1.12.0 Better Generator Terminal Dialogs
Release v1.12.0
2 parents 45e56e2 + 636e536 commit 39e32e3

File tree

20 files changed

+523
-85
lines changed

20 files changed

+523
-85
lines changed

CHANGELOG.rst

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,48 @@ Changelog
33
=========
44

55

6+
1.12.0 (2024-01-18)
7+
===================
8+
9+
In this release, we fix then [known issue](https://github.com/boromir674/cookiecutter-python-package/issues/63),
10+
when running the `Generator` CLI in `interactive` mode.
11+
12+
**Now**, in case the CLI is ran in `interactive` mode, all interactive dialogs / prompts
13+
are delegated to `Questionnaire`, which is already a python dependency of the `Generator`.
14+
15+
The `Cookiecutter` callable / executable is **now** always called, with the `no_input` boolean
16+
flag set to `True`.
17+
18+
The idea was to refrain from updating our locked `cookiecutter` dependency, from 1.7.x to 2.x,
19+
for the time being.
20+
21+
22+
Changes
23+
^^^^^^^
24+
25+
feature
26+
"""""""
27+
- support running interactive CLI, without supplying User's Config Yaml file
28+
- populate Context, in pre_main, if interactive mode is ON
29+
30+
test
31+
""""
32+
- make snapshot testinng robust against runtime Calendar Year changes!
33+
34+
refactor
35+
""""""""
36+
- exclude DEBUG-level logs from being emitted in the Console (just in file)
37+
- Ruff, Black, Isort, Mypy
38+
39+
chore
40+
"""""
41+
- remove Handler Chain Infra, since we delegate handling Input to Questionnaire
42+
43+
release
44+
"""""""
45+
- bump version to 1.12.0
46+
47+
648
1.11.4 (2023-12-25)
749
===================
850

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,9 +272,9 @@ Free/Libre and Open Source Software (FLOSS)
272272

273273
.. Github Releases & Tags
274274
275-
.. |commits_since_specific_tag_on_master| image:: https://img.shields.io/github/commits-since/boromir674/cookiecutter-python-package/v1.11.4/master?color=blue&logo=github
275+
.. |commits_since_specific_tag_on_master| image:: https://img.shields.io/github/commits-since/boromir674/cookiecutter-python-package/v1.12.0/master?color=blue&logo=github
276276
:alt: GitHub commits since tagged version (branch)
277-
:target: https://github.com/boromir674/cookiecutter-python-package/compare/v1.11.4..master
277+
:target: https://github.com/boromir674/cookiecutter-python-package/compare/v1.12.0..master
278278

279279
.. |commits_since_latest_github_release| image:: https://img.shields.io/github/commits-since/boromir674/cookiecutter-python-package/latest?color=blue&logo=semver&sort=semver
280280
:alt: GitHub commits since latest release (by SemVer)

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
author = 'Konstantinos Lampridis'
3131

3232
# The full version, including alpha/beta/rc tags
33-
release = '1.11.4'
33+
release = '1.12.0'
3434

3535
# -- General configuration ---------------------------------------------------
3636

poetry.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ build-backend = "poetry.core.masonry.api"
1010
## Also renders on pypi as 'subtitle'
1111
[tool.poetry]
1212
name = "cookiecutter_python"
13-
version = "1.11.4"
13+
version = "1.12.0"
1414
description = "1-click Generator of Python Project, from Template with streamlined \"DevOps\" using a powerful CI/CD Pipeline."
1515
authors = ["Konstantinos Lampridis <k.lampridis@hotmail.com>"]
1616
maintainers = ["Konstantinos Lampridis <k.lampridis@hotmail.com>"]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
__version__ = '1.11.4'
1+
__version__ = '1.12.0'
22

33
from . import _logging # noqa

src/cookiecutter_python/_logging.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@
5858

5959

6060
### Handler which writes DEBUG messages or higher to the sys.stderr ###
61-
console.setLevel(logging.DEBUG)
62-
# console.setLevel(logging.INFO)
61+
# console.setLevel(logging.DEBUG)
62+
console.setLevel(logging.INFO)
6363

6464

6565
# set a format which is simpler for console use
Lines changed: 86 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,97 @@
1+
import json
12
import logging
23
import typing as t
34

4-
from cookiecutter_python.handle.interpreters_support import handle as get_interpreters
5-
6-
from .load_config import get_interpreters_from_yaml
7-
85
GivenInterpreters = t.Mapping[str, t.Sequence[str]]
96

107
logger = logging.getLogger(__name__)
118

129

13-
def supported_interpreters(config_file: str, no_input: bool) -> t.Optional[GivenInterpreters]:
14-
# Interactive Mode: ask for user input with Interactive (console) Dialog
15-
if not no_input: # render checkbox console ui, and return user selection
16-
# TODO: verify that the below works!
17-
return check_box_dialog(config_file=config_file)
18-
# NON Interactive Mode: try to automatically read interpreters from config
19-
if config_file:
20-
# read 'default_context.interpreters' from User Config yaml file
21-
return get_interpreters_from_yaml(config_file)
22-
## No User Config yaml file, was supplied through CLI parameters, at runtime
23-
return None
10+
def parse_context(config_file: str):
11+
# initialize data
12+
user_context = {}
13+
from pathlib import Path
14+
15+
my_dir = Path(__file__).parent.absolute()
16+
17+
# use whatever way cookiecutter uses to render the cookiecutter.json
18+
# cookie uses Jinja2
19+
20+
from jinja2 import Environment, FileSystemLoader
21+
22+
# render file
23+
env = Environment(
24+
loader=FileSystemLoader(str(my_dir / '..')),
25+
extensions=['jinja2_time.TimeExtension'], # shipped with cookiecutter 1.7
26+
)
27+
template = env.get_template('cookiecutter.json')
28+
rendered = template.render({'cookiecutter': {}})
29+
30+
assert isinstance(rendered, str)
2431

32+
cook_json: t.Mapping[str, t.Any] = json.loads(rendered)
33+
34+
# in cookiecutter 1.7, a 'choice' variable is a json array
35+
choices = {k: v for k, v in cook_json.items() if isinstance(v, list)}
36+
assert 'rtd_python_version' in choices, f"KEYS: {choices.keys()}"
37+
choice_defaults = {k: v[0] for k, v in choices.items()}
38+
39+
# start
40+
c = cook_json['interpreters']
41+
42+
cookie_defaults = dict(cook_json, **choice_defaults)
2543

26-
def check_box_dialog(config_file: t.Optional[str] = None) -> GivenInterpreters:
27-
defaults: t.Optional[t.Sequence[str]] = None
2844
if config_file:
29-
interpreters_data: t.Optional[GivenInterpreters] = get_interpreters_from_yaml(
30-
config_file
31-
)
32-
if interpreters_data:
33-
defaults = interpreters_data.get('supported-interpreters', None)
34-
return get_interpreters(choices=defaults)
45+
from .user_config_proxy import get_user_config
46+
47+
data = get_user_config(config_file, default_config=False)
48+
# data = load_yaml(config_file)
49+
user_context = data['default_context']
50+
c = json.loads(user_context.get('interpreters', '{}'))
51+
52+
context_defaults = dict(cookie_defaults, **user_context)
53+
54+
from cookiecutter_python.handle.interactive_cli_pipeline import (
55+
InteractiveDialogsPipeline,
56+
)
57+
58+
pipe = InteractiveDialogsPipeline()
59+
60+
# c = json.loads(context_defaults.get('interpreters', '{}'))
61+
cc = c.get('supported-interpreters', ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"])
62+
# assert choices['rtd_python_version'] == [], f"DEBUG: {choices['rtd_python_version']}"
63+
res = pipe.process(
64+
[
65+
{
66+
"project_name": context_defaults['project_name'],
67+
"project_type": {
68+
'default': context_defaults['project_type'],
69+
'choices': choices['project_type'],
70+
},
71+
"full_name": context_defaults['full_name'],
72+
"author_email": context_defaults['author_email'],
73+
"github_username": context_defaults['github_username'],
74+
"project_short_description": context_defaults['project_short_description'],
75+
# "release_date": context_defaults['release_date'],
76+
# "year": context_defaults['year'],
77+
"version": context_defaults['version'],
78+
"initialize_git_repo": {
79+
'default': context_defaults['initialize_git_repo'],
80+
'choices': choices['initialize_git_repo'],
81+
},
82+
"supported-interpreters": {
83+
# 'default': context_defaults['initialize_git_repo'],
84+
'choices': [(choice, True) for choice in cc],
85+
},
86+
"docs_builder": {
87+
'default': context_defaults['docs_builder'],
88+
'choices': choices['docs_builder'],
89+
},
90+
"rtd_python_version": {
91+
'default': context_defaults['rtd_python_version'],
92+
'choices': choices['rtd_python_version'],
93+
},
94+
}
95+
]
96+
)
97+
return res

src/cookiecutter_python/backend/hosting_services/check_engine.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,28 @@ def check(self, servers: List[str]):
5353
return iter(filter(None, [getattr(self, server)() for server in servers]))
5454

5555
@staticmethod
56-
def create(config_file, default_config):
56+
def create(config_file: str, default_config: bool):
57+
"""Initialize objects, for Asynchronous http with 3rd-party Services
58+
59+
Objects are designed to Ask PyPI and Read The Docs, if the soon to be
60+
generated package name, and readthedocs project slug are available.
61+
62+
These 'Checker' objects make asynchronous http requests to PyPI and RTD
63+
web servers, for non-blocking IO, and to avoid blocking the main thread.
64+
65+
Checkers are initialized as 'Activated' if User Config is given and
66+
Default Config is False.
67+
68+
Then each Checker (pypi, rtd) requires:
69+
- PyPI requires the 'pkg_name' in User's yaml Config
70+
- RTD requires the 'readthedocs_project_slug' in User's yaml Config
71+
72+
to derive the URLs for Future Requests
73+
74+
Args:
75+
config_file (str): user's yaml config file
76+
default_config (bool): default config flag
77+
"""
5778
return Engine(
5879
config_file,
5980
default_config,

src/cookiecutter_python/backend/main.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,20 @@
1515

1616

1717
def generate(
18-
checkout=None,
19-
no_input=False,
18+
# interactive=True,
19+
no_input=False, # INTERACTIVE ON by Default
2020
extra_context=None,
2121
replay=False,
2222
overwrite=False,
2323
output_dir='.',
2424
config_file=None,
25+
skip_if_file_exists=False,
26+
# deprecated
2527
default_config=False,
2628
password=None,
2729
directory=None,
28-
skip_if_file_exists=False,
30+
checkout=None,
31+
###
2932
) -> str:
3033
"""Create Python Project, with CI/CD pipeline, from the project template.
3134
@@ -39,7 +42,6 @@ def generate(
3942
# - prompt for user input in interactive or atempt to read from yaml otherwise
4043
# - prepare Cookiecutter extra context:
4144
# - add interpreters versions list
42-
# - store 'docs' folder, per docs builder, that Generator supports
4345
request = pre_main(
4446
Request(
4547
config_file=config_file,
@@ -54,7 +56,8 @@ def generate(
5456
project_dir = generator(
5557
os.path.abspath(os.path.join(my_dir, '..')), # template dir path
5658
checkout=checkout,
57-
no_input=no_input,
59+
# no_input=no_input,
60+
no_input=True,
5861
# we pass the Request computed context in the Cookiecutter Extra Context
5962
# if extra_context includes a 'supported-interpreters' key:
6063
# no_input == True: automatic generation of CI Test Matrix Python Interpreters versions list, should happen

0 commit comments

Comments
 (0)