Skip to content

Commit

Permalink
[NEW] Migrating Away from setup.cfg
Browse files Browse the repository at this point in the history
  • Loading branch information
boromir674 committed May 27, 2022
2 parents 223e65b + ca8896a commit 5c78034
Show file tree
Hide file tree
Showing 14 changed files with 321 additions and 359 deletions.
37 changes: 37 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Install the *cookiecutter-python* package with *python3 -m pip install cookiecutter-python*
2. Run the **cli** as: *generate-python ...*
please provided the arguments supplied to the cli
3. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
- OS: [e.g. Linux Mint, iOS, Windows]
- Version [e.g. 1.2.0]
run: *generate-python --version*
- Python Interpreter
run: *generate-python --version*


**Additional context**
Add any other context about the problem here.
For example, what input values were supplied to the *generate-python*
cli (ie paste contents of your *config file*)
20 changes: 20 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[FEATURE]"
labels: enhancement
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.
17 changes: 17 additions & 0 deletions .github/ISSUE_TEMPLATE/issue-inquiry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
name: Issue Inquiry
about: Make an inquiry about an "issue" which is not a 'bug' nor a 'feature request'
title: "[OTHER ISSUE]"
labels: question
assignees: ''

---

**TODO: Title Goes Here**

**Do you have a question related to the *cookiecutter-python* package?**
Please submit your question :)

Examples:
- Why is *A* designed the way it is designed?
- How does *A* accomplish whatever it accomplishes?
2 changes: 1 addition & 1 deletion .github/biskotaki.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ default_context:
author_email: [email protected]
github_username: boromir674
project_short_description: Project generated from the https://github.com/boromir674/cookiecutter-python-package/tree/master/src/cookiecutter_python cookiecutter
initialize_git_repo: yes
initialize_git_repo: no
18 changes: 10 additions & 8 deletions .github/workflows/generate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- 'develop'
- update-generate
pull_request:
branches:
- 'master'
Expand All @@ -13,7 +14,7 @@ jobs:
generate:
runs-on: ubuntu-latest
env:
branch: apply-cookiecutter-python-package-template
branch: auto-generated
SUPER_SECRET: ${{ secrets.BISKOTAKI_GH_TOKEN }}
# GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
Expand Down Expand Up @@ -71,31 +72,32 @@ jobs:
sudo apt update -y && sudo apt install -y aptitude
sudo aptitude install -y graphviz
python -m pip install --upgrade pip
python -m pip install cookiecutter tox
python -m pip install tox
- name: Clone Biskotaki repo
uses: actions/checkout@v3
with:
repository: boromir674/biskotaki
ref: ${{ env.branch }}
path: 'cloned/biskotaki'

- name: Find Biskotaki version
run: |
cd cloned/biskotaki
chmod +x ./scripts/parse_version.py
echo "PKG_VERSION=$(scripts/parse_version.py)" >> $GITHUB_ENV
echo "PKG_VERSION=$(python scripts/parse_version.py)" >> $GITHUB_ENV
- name: Generate Biskotaki from Template
run: |
pip install --user -e .
echo " version: \"${{ env.PKG_VERSION }}\"" >> .github/biskotaki.yaml
generate-python -o gen --config-file ./.github/biskotaki.yaml --no-input
generate-python -o gen --config-file ./.github/biskotaki.yaml --no-input -f
- name: Test against multiple 'installation' scenarios
- name: Test distributions 'Source', 'Built' and 'edit' (dev env) mode installation
run: |
cd gen/biskotaki
tox -e "py38{-path, -sdist, -wheel, -dev}"
- name: Test Operations such as 'build process' and 'linting'
tox -e "py38{-sdist, -wheel, -dev}"
tox -e coverage
- name: Run Lint, Type Check, Build, Pyroma and PyDeps commands
run: |
cd gen/biskotaki
tox -e lint
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@
Changelog
=========

1.2.1 (2022-05-27)
==================

Compeltely migrate away from *setup.cfg*.
Add Issue Templates, as markdown files, to help create well documented Issues on github.

Changes
^^^^^^^

ci
""
- do not run py38-path tox env & improve ci steps names
- push generated package to 'auto-generated' branch on 'origin' remote
- do not initialize a git repository after package generation
- refactor parse script to read from pyproject.toml


1.2.0 (2022-05-24)
==================

Expand Down
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,9 @@ For more complex use cases, you can modify the Template and also leverage all of

.. Github Releases & Tags
.. |commits_since_specific_tag_on_master| image:: https://img.shields.io/github/commits-since/boromir674/cookiecutter-python-package/v1.2.0/master?color=blue&logo=github
.. |commits_since_specific_tag_on_master| image:: https://img.shields.io/github/commits-since/boromir674/cookiecutter-python-package/v1.2.1/master?color=blue&logo=github
:alt: GitHub commits since tagged version (branch)
:target: https://github.com/boromir674/cookiecutter-python-package/compare/v1.2.0..master
:target: https://github.com/boromir674/cookiecutter-python-package/compare/v1.2.1..master

.. |commits_since_latest_github_release| image:: https://img.shields.io/github/commits-since/boromir674/cookiecutter-python-package/latest?color=blue&logo=semver&sort=semver
:alt: GitHub commits since latest release (by SemVer)
Expand Down
6 changes: 5 additions & 1 deletion pyproject.toml
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ build-backend = "poetry.core.masonry.api"
## Also renders on pypi as 'subtitle'
[tool.poetry]
name = "cookiecutter_python"
version = "1.2.0"
version = "1.2.1"
description = "Yet another modern Python Package (pypi) with emphasis in CI/CD and automation."
authors = ["Konstantinos Lampridis <[email protected]>"]
maintainers = ["Konstantinos Lampridis <[email protected]>"]
Expand Down Expand Up @@ -190,3 +190,7 @@ hooks/post_gen_project.py

[tool.isort]
profile = 'black'


[tool.software-release]
version_variable = "src/cookiecutter_python/__init__.py:__version__"
143 changes: 105 additions & 38 deletions scripts/parse_version.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,126 @@
#!/usr/bin/env python


import os
import re
import sys
import typing as t

# TODO Improve: try using the semantic_version_checker package for semver regex

ExceptionFactory = t.Callable[[str, str, str], Exception]
ClientCallback = t.Callable[[str, str], t.Tuple]

MatchConverter = t.Callable[[t.Match], t.Tuple]
MatchData = t.Tuple[str, t.List[t.Any], t.Callable[[t.Match], t.Tuple]]
# 1st item (str): 'method'/'callable attribute' of the 're' python module)
# 2nd item (list): zero or more additional runtime arguments
# 3rd item (Callable): takes a Match object and return a tuple of strings

my_dir = os.path.dirname(os.path.realpath(__file__))

SETUP_CFG_FILENAME = 'setup.cfg'
SETUP_CFG = os.path.join(my_dir, '../', SETUP_CFG_FILENAME)
TOML = 'pyproject.toml'
TOML_FILE = os.path.abspath(os.path.join(my_dir, '..', TOML))

DEMO_SECTION: str = (
"[tool.software-release]\nversion_variable = "
"src/package_name/__init__.py:__version__"
)

def main():
"""Get the package version string provided that the developer has setup indication how to find it. Reads the
[semantic_release] section found in setup.cfg and then determines where is the actual version string
"""
# Automatically compute package version from the [semantic_release] section in setup.cfg
with open(SETUP_CFG, 'r') as _file:
regex = r"\[semantic_release\][\w\s=/\.:\d]+version_variable[\ \t]*=[\ \t]*([\w\.]+(?:/[\w\.]+)*):(\w+)"
match = re.search(regex, _file.read(), re.MULTILINE)

def build_client_callback(data: MatchData, factory: ExceptionFactory) -> ClientCallback:

def client_callback(file_path: str, regex: str) -> t.Tuple:
with open(file_path, 'r') as _file:
contents = _file.read()
match = getattr(re, data[0])(regex, contents, *data[1])
if match:
file_with_version_string = os.path.join(my_dir, '../', match.group(1))
variable_holding_version_value = match.group(2)
extracted_tuple = data[2](match)
return extracted_tuple
else:
raise RuntimeError(
f"Expected to find the '[semantic_release]' section, in the '{SETUP_CFG}' file, with key "
f"'version_variable'.\nFor example:\n[semantic_release]\nversion_variable = "
f"src/package_name/__init__.py:__version__\n indicated that the version string should be looked up in "
f"the src/package_name/__init__.py file registered under the __version__ 'name'")

# (it does not have to be a.py file)
# to indicate that the version is stored in the '__version__'
if not os.path.isfile(file_with_version_string):
raise FileNotFoundError(
f"Path '{file_with_version_string} does not appear to be valid. Please go to the '{SETUP_CFG}' file, at the"
f" [semantic_release] section and set the 'version_variable' key with a valid file path (to look for the "
f"version string)")
raise factory(file_path, regex, contents)
return client_callback

reg_string = r'\s*=\s*[\'\"]([^\'\"]*)[\'\"]'

# PARSERS

with open(file_with_version_string, 'r') as _file:
content = _file.read()
reg = f'^{variable_holding_version_value}' + reg_string
match = re.search(reg, content, re.MULTILINE)
if match:
_version = match.group(1)
return _version
raise AttributeError(f"Could not find a match for regex {reg} when applied to:\n{content}")
software_release_parser = build_client_callback((
'search',
[re.MULTILINE,],
lambda match: (match.group(1), match.group(2))
),
lambda file_path, reg, string: RuntimeError(
"Expected to find the '[tool.software-release]' section, in "
f"the '{file_path}' file, with key "
"'version_variable'.\nFor example:\n"
f"{DEMO_SECTION}\n "
"indicates that the version string should be looked up in "
f"the src/package_name/__init__.py file and specifically "
"a '__version__ = 1.2.3' kind of line is expected to be found."
)
)


if __name__ == '__main__':
version_file_parser = build_client_callback((
'search',
[re.MULTILINE,],
lambda match: (match.group(1),)
),
lambda file_path, reg, string: AttributeError(
"Could not find a match for regex {regex} when applied to:".format(
regex=reg
) + "\n{content}".format(content=string)
)
)


def parse_version(software_release_cfg: str) -> str:
"""Detect, parse and return the version (string) from python source code.
Get the package version (string) provided that the developer has setup
indication how to find it.
Reads the [tool.software-release] section found in pyproject.toml and then
determines where is the actual version string.
"""
header = r'\[tool\.software-release\]'
sep = r'[\w\s=/\.:\d]+' # in some cases accounts for miss-typed characters!
version_specification = \
r"version_variable[\ \t]*=[\ \t]*['\"]?([\w\.]+(?:/[\w\.]+)*):(\w+)['\"]?"
regex = f"{header}{sep}{version_specification}"

file_name_with_version, version_variable_name = \
software_release_parser(software_release_cfg, regex)

file_with_version_string = \
os.path.abspath(os.path.join(my_dir, '../', file_name_with_version))

if not os.path.isfile(file_with_version_string):
raise FileNotFoundError(
f"Path '{file_with_version_string} does not appear to be valid. "
f"Please go to the '{software_release_cfg}' file, at the"
" [tool.software-release] section and set the 'version_variable' "
"key with a valid file path (to look for the version string). "
f"For example:\n{DEMO_SECTION}\n"
)

reg = f'^{version_variable_name}' + r'\s*=\s*[\'\"]([^\'\"]*)[\'\"]'
version, = version_file_parser(file_with_version_string, reg)
return version


def _main():
try:
version_string = main()
version_string = parse_version(TOML_FILE)
print(version_string)
return 0
except (RuntimeError, FileNotFoundError, AttributeError) as exception:
print(exception)
sys.exit(1)
return 1


def main():
sys.exit(_main())


if __name__ == '__main__':
main()
Loading

0 comments on commit 5c78034

Please sign in to comment.