spreen-pr 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
spreen_pr/__init__.py ADDED
@@ -0,0 +1,13 @@
1
+ """Spreens a pull request — the falcon's stoop, then the preen: derives a
2
+ PR title and labels from a topic branch name shaped
3
+ ``{user}/{issue}/{category}/{summary}`` (with a hotfix special case) and
4
+ prints them as plain text or JSON."""
5
+
6
+ from .application import Application
7
+
8
+ __version__ = '0.1.0'
9
+
10
+ __all__ = [
11
+ 'Application',
12
+ '__version__',
13
+ ]
@@ -0,0 +1,49 @@
1
+ import re
2
+
3
+
4
+ class Application:
5
+ """Derives a pull request title and labels from a topic branch name
6
+ shaped ``{user}/{issue}/{category}/{summary}``, with the hotfix special
7
+ case ``{user}/{issue}/hotfix/{category}/{summary}``."""
8
+
9
+ EXPECTED_SHAPE = '{user}/{issue}/{category}/{summary}'
10
+
11
+ @classmethod
12
+ def run(cls, branch_name: str) -> str:
13
+ return cls(branch_name=branch_name).title
14
+
15
+ def __init__(self, branch_name: str) -> None:
16
+ self._branch_name = branch_name
17
+ self._segments = branch_name.split('/')
18
+ self._validate()
19
+
20
+ @property
21
+ def title(self) -> str:
22
+ labels = ''.join(f'[{label}]' for label in self.labels)
23
+ return f'{labels} {self.topic}'
24
+
25
+ @property
26
+ def labels(self) -> list[str]:
27
+ if self._is_hotfix():
28
+ return [self._segments[-3].capitalize(), self._segments[-2]]
29
+ return [self._segments[-2]]
30
+
31
+ @property
32
+ def topic(self) -> str:
33
+ words = re.split(r'[-_]', self._segments[-1])
34
+ return ' '.join(word.capitalize() for word in words)
35
+
36
+ def to_dict(self) -> dict[str, object]:
37
+ return {'title': self.title, 'labels': self.labels}
38
+
39
+ # private
40
+
41
+ def _is_hotfix(self) -> bool:
42
+ return self._segments[-3] == 'hotfix'
43
+
44
+ def _validate(self) -> None:
45
+ if len(self._segments) >= 3 and all(self._segments):
46
+ return
47
+ raise ValueError(
48
+ f'Branch name `{self._branch_name}` does not match the expected '
49
+ f'`{self.EXPECTED_SHAPE}` shape')
spreen_pr/cli.py ADDED
@@ -0,0 +1,56 @@
1
+ """Command line interface behind the `pr-title` executable:
2
+ `pr-title [BRANCH_NAME] [--format text|json]`."""
3
+
4
+ import argparse
5
+ import json
6
+ import subprocess
7
+ import sys
8
+
9
+ from . import __version__
10
+ from .application import Application
11
+
12
+
13
+ def _build_parser() -> argparse.ArgumentParser:
14
+ parser = argparse.ArgumentParser(
15
+ prog='pr-title',
16
+ description='Print a pull request title with labels derived from '
17
+ 'a topic branch name.')
18
+ parser.add_argument(
19
+ 'branch_name', nargs='?', metavar='BRANCH_NAME',
20
+ help='Topic branch name (default: the current git branch)')
21
+ parser.add_argument(
22
+ '--format', choices=('text', 'json'), default='text',
23
+ help='Output format (default: text)')
24
+ parser.add_argument('--version', action='version', version=__version__)
25
+ return parser
26
+
27
+
28
+ def _current_git_branch() -> str:
29
+ try:
30
+ return subprocess.run(
31
+ ['git', 'branch', '--show-current'],
32
+ capture_output=True, text=True, check=False).stdout.strip()
33
+ except OSError:
34
+ return ''
35
+
36
+
37
+ def main(argv: list[str] | None = None) -> int:
38
+ args = _build_parser().parse_args(argv)
39
+ try:
40
+ branch_name = args.branch_name or _current_git_branch()
41
+ if not branch_name:
42
+ raise ValueError('No branch name given and the current git '
43
+ 'branch could not be detected')
44
+ application = Application(branch_name=branch_name)
45
+ except ValueError as error:
46
+ print(error, file=sys.stderr)
47
+ return 1
48
+ if args.format == 'json':
49
+ print(json.dumps(application.to_dict()))
50
+ else:
51
+ print(application.title)
52
+ return 0
53
+
54
+
55
+ if __name__ == '__main__':
56
+ sys.exit(main())
spreen_pr/py.typed ADDED
File without changes
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.4
2
+ Name: spreen-pr
3
+ Version: 0.1.0
4
+ Summary: Print a pull request title with labels derived from the topic branch name.
5
+ Author: hayat01sh1da
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/hayat01sh1da/spreen-pr
8
+ Project-URL: Source, https://github.com/hayat01sh1da/spreen-pr
9
+ Project-URL: Changelog, https://github.com/hayat01sh1da/spreen-pr/blob/master/CHANGELOG.md
10
+ Keywords: github,pull-request,title,label,branch
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Software Development :: Version Control :: Git
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE.txt
20
+ Dynamic: license-file
21
+
22
+ ## 1. Environment
23
+
24
+ - Python 3.14.6
25
+ - pip 26.1.2
26
+
27
+ ## 2. Installation
28
+
29
+ ```command
30
+ $ pipx install spreen-pr
31
+ ```
32
+
33
+ (`pip install spreen-pr` works too if you prefer managing the environment yourself.)
34
+
35
+ For development, install the dependencies via requirements.txt:
36
+
37
+ ```command
38
+ $ pip install -r requirements.txt
39
+ ```
40
+
41
+ ## 3. Execution
42
+
43
+ ```command
44
+ $ pr-title hayat01sh1da/issue-89/service/improve-onboarding-flow
45
+ [service] Improve Onboarding Flow
46
+ ```
47
+
48
+ With no argument, the current git branch is used:
49
+
50
+ ```command
51
+ $ git switch hayat01sh1da/issue-90/hotfix/service/fix_login_crash
52
+ $ pr-title
53
+ [Hotfix][service] Fix Login Crash
54
+ ```
55
+
56
+ `--format json` exposes the labels separately for scripting and CI:
57
+
58
+ ```command
59
+ $ pr-title hayat01sh1da/issue-90/hotfix/service/fix_login_crash --format json
60
+ {"title": "[Hotfix][service] Fix Login Crash", "labels": ["Hotfix", "service"]}
61
+ ```
62
+
63
+ As a library:
64
+
65
+ ```python
66
+ from spreen_pr import Application
67
+
68
+ Application.run(branch_name='hayat01sh1da/issue-89/service/improve-onboarding-flow')
69
+ # => '[service] Improve Onboarding Flow'
70
+
71
+ application = Application(branch_name='hayat01sh1da/issue-90/hotfix/service/fix_login_crash')
72
+ application.title # => '[Hotfix][service] Fix Login Crash'
73
+ application.labels # => ['Hotfix', 'service']
74
+ ```
75
+
76
+ ## 4. Unit Test
77
+
78
+ ```command
79
+ $ pytest
80
+ ============================= test session starts ==============================
81
+ platform linux -- Python 3.14.6, pytest-9.1.1, pluggy-1.6.0
82
+ rootdir: spreen-pr/pypi
83
+ configfile: pyproject.toml
84
+ collected 15 items
85
+
86
+ test/test_application.py ......... [ 60%]
87
+ test/test_cli.py ...... [100%]
88
+
89
+ ============================== 15 passed in 0.12s ===============================
90
+ ```
91
+
92
+ ## 5. Static Code Analysis
93
+
94
+ ```command
95
+ $ flake8 .
96
+ $ autoflake8 --in-place --remove-duplicate-keys --remove-unused-variables --recursive .
97
+ $ autopep8 --in-place --aggressive --aggressive --recursive .
98
+ ```
99
+
100
+ ## 6. Type Checks
101
+
102
+ ```command
103
+ $ mypy .
104
+ Success: no issues found in 6 source files
105
+ ```
106
+
107
+ ## 7. Build
108
+
109
+ ```command
110
+ $ python -m build
111
+ $ pipx install ./dist/spreen_pr-0.1.0-py3-none-any.whl
112
+ ```
@@ -0,0 +1,10 @@
1
+ spreen_pr/__init__.py,sha256=mHTZzI4rUi51IDrl_fmc91mAVfCmyw2PvQup6UhqJgk,353
2
+ spreen_pr/application.py,sha256=sdfe9odAHZOWU0LsoFz0pN8KkJWG4u6rEWjpxQDGH5g,1533
3
+ spreen_pr/cli.py,sha256=fL1OT6j7AwiUpF7m5WGghPEsEJ8xsPm2D0sl9Nmfmtw,1736
4
+ spreen_pr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ spreen_pr-0.1.0.dist-info/licenses/LICENSE.txt,sha256=SAKjouX-73Vh5lFWG4Zy3nl79mjfvZGmx7P7aklh05o,1080
6
+ spreen_pr-0.1.0.dist-info/METADATA,sha256=xhdaf5Mz3FDCdcLyu0aHtUYmThv2u30mjDWFBhwp5HE,2996
7
+ spreen_pr-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
8
+ spreen_pr-0.1.0.dist-info/entry_points.txt,sha256=km019rGPx2xSyjoXlKZOWBDnJSuf4oyvXhTN8zIUJ-k,48
9
+ spreen_pr-0.1.0.dist-info/top_level.txt,sha256=69Rmty0_oLglda21aLeKNJ3ZLIRZ7hXPfyBfdKF0i78,10
10
+ spreen_pr-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ pr-title = spreen_pr.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 - present @hayat01sh1da
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ spreen_pr