Skip to content

Commit 42e6490

Browse files
[PATCH] Minor fixes for PEP-621 refactor.
Changes in file .gitattributes: * overhauled with a more modern template from y.m.m.v. Changes in file docs/conf.py: * related work Changes in file multicast/__init__.py: * refactored to use cli() instead of main() as entry point * related work Changes in file multicast/__main__.py: * implemented a new entry-point cli() function * some opportunistic refactoring * related work Changes in file pyproject.toml: * switched to use cli function as entry-point
1 parent c5c7c7d commit 42e6490

File tree

5 files changed

+149
-45
lines changed

5 files changed

+149
-45
lines changed

.gitattributes

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,44 @@
1-
* text=auto
2-
*.cfg,*.conf text working-tree-encoding=UTF-8 diff=config
3-
*.txt text working-tree-encoding=UTF-8
4-
*.py text working-tree-encoding=UTF-8 diff=python merge=python
5-
multicast/*.py text working-tree-encoding=UTF-8 diff=python merge=python
6-
tests/*.py text working-tree-encoding=UTF-8 diff=python merge=python
7-
*.pyc -text
8-
*.sh text working-tree-encoding=UTF-8 diff=shell merge=shell
9-
*.bash text working-tree-encoding=UTF-8 diff=shell merge=shell
10-
*.ini text
11-
*.md text working-tree-encoding=UTF-8 diff=markdown merge=markdown
12-
*.yml text
13-
*.jpg -text
14-
*.png -text
15-
*.conf text diff=config
16-
*.bat text
17-
*.rst text
18-
tests/check_* text working-tree-encoding=UTF-8 diff=shell merge=shell
19-
Makefile text working-tree-encoding=UTF-8 diff=makefile merge=makefile
1+
# from -- https://github.com/reactive-firewall/ymmv.git
2+
3+
# Set default behaviour to automatically normalize line endings.
4+
* text=auto
5+
6+
# Force batch scripts to always use CRLF line endings so that if a repo is accessed
7+
# in Windows via a file share from Linux, the scripts will work.
8+
*.{cmd,[cC][mM][dD]} text eol=crlf
9+
*.{bat,[bB][aA][tT]} text eol=crlf
10+
11+
# Force bash scripts to always use LF line endings so that if a repo is accessed
12+
# in Unix via a file share from Windows, the scripts will work.
13+
*.{ash,[aA][sS][hH]} text eol=lf diff=bash merge=bash working-tree-encoding=UTF-8
14+
*.{bash,[bB][aA][sS][hH]} text eol=lf diff=bash merge=bash working-tree-encoding=UTF-8
15+
*.{csh,[cC][sS][hH]} text eol=lf diff=bash merge=bash working-tree-encoding=UTF-8
16+
*.{dash,[dD][aA][sS][hH]} text eol=lf diff=bash merge=bash working-tree-encoding=UTF-8
17+
*.{sh,[sS][hH]} text eol=lf diff=bash merge=bash working-tree-encoding=UTF-8
18+
*.{zsh,[zZ][sS][hH]} text eol=lf diff=bash merge=bash working-tree-encoding=UTF-8
19+
20+
*.{cfg,[cC][fF][gG]},*.{conf,[cC][oO][nN][fF]} text eol=lf diff=config working-tree-encoding=UTF-8
21+
*.{toml,[tT][oO][mM][lL]} text eol=lf working-tree-encoding=UTF-8
22+
*.{ini,[iI][nN][iI]} text eol=lf working-tree-encoding=UTF-8
23+
*.{yml,[yY][mM][lL]},*.{yaml,[yY][aA][mM][lL]} text eol=lf working-tree-encoding=UTF-8
24+
*.{txt,[tT][xX][tT]} text eol=lf diff=markdown working-tree-encoding=UTF-8
25+
*.{rst,[rR][sS][tT]} text eol=lf working-tree-encoding=UTF-8
26+
*.{md,[mM][dD]},*.{markdown,[mM][aA][rR][kK][dD][oO][wW][nN]} text eol=lf diff=markdown merge=markdown working-tree-encoding=UTF-8
27+
28+
*.py text eol=lf diff=python merge=python working-tree-encoding=UTF-8
29+
*.pyc export-ignore diff=python -text
30+
*.pyi export-ignore text eol=lf diff=python merge=python working-tree-encoding=UTF-8
31+
multicast/*.py text eol=lf diff=python merge=python working-tree-encoding=UTF-8
32+
tests/*.py text eol=lf diff=python merge=python working-tree-encoding=UTF-8
33+
docs/*.py text eol=lf diff=python merge=python working-tree-encoding=UTF-8
34+
tests/check_* text eol=lf diff=bash merge=bash working-tree-encoding=UTF-8
35+
36+
*.jpg -text
37+
*.png -text
38+
*.{svg,[sS][vV][gG]} text eol=lf diff=html merge=html working-tree-encoding=UTF-8
39+
40+
*.{rst,[rR][sS][tT]} text eol=lf working-tree-encoding=UTF-8
41+
42+
# more rules
43+
.DS_Store export-ignore -text
44+
Makefile text eol=lf diff=makefile merge=makefile working-tree-encoding=UTF-8

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@
125125
# The short X.Y version.
126126
version = "v2.0"
127127
# The full version, including alpha/beta/rc tags.
128-
release = "v2.0.9a3"
128+
release = "v2.0.9a4"
129129

130130
# The language for content autogenerated by Sphinx. Refer to documentation
131131
# for a list of supported languages.

multicast/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@
221221

222222
global __version__ # skipcq: PYL-W0604
223223

224-
__version__ = "2.0.9-alpha-3"
224+
__version__ = "2.0.9-alpha-4"
225225
"""The version of this program.
226226
227227
The `__version__` attribute, like many dunder attributes, is associated with the implementation
@@ -1047,6 +1047,6 @@ def doStep(self, *args, **kwargs) -> tuple: # pragma: no cover
10471047

10481048
if __name__ in "__main__":
10491049
__EXIT_CODE = 2
1050-
if __main__.main is not None:
1051-
__EXIT_CODE = __main__.main(sys.argv[1:])
1050+
if __main__.cli is not None:
1051+
__EXIT_CODE = __main__.cli()
10521052
exit(__EXIT_CODE) # skipcq: PYL-R1722 - intentionally allow coverage and testing to catch exit

multicast/__main__.py

Lines changed: 99 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -574,21 +574,21 @@ class McastDispatch(mtool):
574574
575575
"""
576576

577-
__proc__ = "multicast"
577+
__proc__: str = "multicast"
578578

579-
__prologue__ = "The Main Entrypoint."
579+
__prologue__: str = "The Main Entrypoint."
580580

581-
__epilogue__ = "Called from the command line, the __main__ component handles the CLI dispatch."
581+
__epilogue__: str = "Called from the command line, this main component handles the CLI dispatch."
582582

583583
@classmethod
584-
def setupArgs(cls, parser):
584+
def setupArgs(cls, parser) -> None:
585585
if parser is not None: # pragma: no branch
586586
for sub_tool in sorted(TASK_OPTIONS.keys()):
587587
sub_parser = parser.add_parser(sub_tool, help="...")
588588
type(TASK_OPTIONS[sub_tool]).setupArgs(sub_parser)
589589

590590
@staticmethod
591-
def useTool(tool, **kwargs):
591+
def useTool(tool, **kwargs) -> tuple:
592592
"""Will Handle launching the actual task functions."""
593593
theResult = None
594594
cached_list = sorted(TASK_OPTIONS.keys())
@@ -601,7 +601,7 @@ def useTool(tool, **kwargs):
601601
_is_done = False
602602
return (_is_done, theResult) # noqa
603603

604-
def doStep(self, *args, **kwargs):
604+
def doStep(self, *args, **kwargs) -> tuple:
605605
"""
606606
Executes the multicast tool based on parsed arguments.
607607
@@ -614,21 +614,21 @@ def doStep(self, *args, **kwargs):
614614
Returns:
615615
A tuple containing the exit status and the result of the tool execution.
616616
"""
617-
__EXIT_MSG = (64, exceptions.EXIT_CODES[64][1])
617+
_response: tuple = (64, exceptions.EXIT_CODES[64][1])
618618
try:
619619
(argz, _) = type(self).parseArgs(*args)
620620
service_cmd = argz.cmd_tool
621621
argz.__dict__.__delitem__("cmd_tool")
622622
_TOOL_MSG = (self.useTool(service_cmd, **argz.__dict__))
623623
if _TOOL_MSG[0]:
624-
__EXIT_MSG = (0, _TOOL_MSG)
624+
_response = (0, _TOOL_MSG)
625625
else:
626-
__EXIT_MSG = (70, _TOOL_MSG)
626+
_response = (70, _TOOL_MSG)
627627
if (sys.stdout.isatty()): # pragma: no cover
628628
print(str(_TOOL_MSG))
629629
except Exception as _cause: # pragma: no branch
630630
exit_code = exceptions.get_exit_code_from_exception(_cause)
631-
__EXIT_MSG = (
631+
_response = (
632632
1,
633633
f"CRITICAL - Attempted '[{__name__}]: {args}' just failed! :: {exit_code} {_cause}" # noqa
634634
)
@@ -638,21 +638,23 @@ def doStep(self, *args, **kwargs):
638638
file=sys.stderr,
639639
)
640640
print(f"{exceptions.EXIT_CODES[exit_code][1]}: {_cause}\n{_cause.args}", file=sys.stderr)
641-
return __EXIT_MSG # noqa
641+
return _response # noqa
642642

643643

644644
@exceptions.exit_on_exception
645-
def main(*argv):
645+
def main(*argv) -> tuple:
646646
"""
647647
Do main event stuff.
648648
649649
Executes the multicast command-line interface, by parsing command-line arguments and dispatching
650650
the appropriate multicast operations.
651651
652-
The main(*args) function in multicast is expected to return a POSIX compatible exit code.
653-
Regardless of errors the result as an 'exit code' (int) is returned.
654-
The only exception is multicast.__main__.main(*args) which will exit with the underlying
655-
return codes.
652+
The main(*args) function in multicast is expected to return a POSIX compatible exit code and
653+
optional detail message. Regardless of errors the result as an 'exit code' (int) is returned,
654+
even if the optional details are not (eg. `tuple(int(exit_code), None)`).
655+
The only exception is when the error results in exiting the process, which will exit the
656+
python runtime with the underlying return codes, instead of returning directly to the then
657+
unreachable caller. See `multicast.exceptions.exit_on_exception` for the mechanisms involved.
656658
The expected return codes are as follows:
657659
= 0: Any nominal state (i.e. no errors and possibly success)
658660
≥ 1: Any erroneous state (includes simple failure)
@@ -748,14 +750,91 @@ def main(*argv):
748750
749751
750752
"""
751-
dispatch = McastDispatch()
753+
dispatch: McastDispatch = McastDispatch()
752754
return dispatch(*argv)
753755

754756

755-
if __name__ in '__main__':
756-
__EXIT_CODE = (1, exceptions.EXIT_CODES[1][1])
757+
def cli() -> int:
758+
"""
759+
Do main console script stuff.
760+
761+
Along with `main()`, `cli()` provides a main entrypoint for console usage of multicast.
762+
763+
`cli()` just calls `main()` with arguments from `sys.argv` and returns only the exit-code.
764+
765+
Through calling `multicast.__main__.main(sys.argv[1:])`, `cli()` ...
766+
> Executes the multicast command-line interface, by parsing command-line arguments and
767+
> dispatching the appropriate multicast operations.
768+
>
769+
> The main(*args) function in multicast is expected to return a POSIX compatible exit code...
770+
771+
`cli()` versus `main(*args)`:
772+
- The primary difference is the return types, whereas `main(*args)` returns a `tuple`,
773+
`cli()` returns only the first element as an `int`.
774+
- The secondary difference between `cli()` and `main(*args)` is that `main(*args)` requires
775+
arguments to be passed, whereas `cli()` will use `sys.argv` instead.
776+
777+
__Except__ in the case of errors, the result as an 'exit code' (int) is returned by `cli()`.
778+
The expected return codes are the same as those from `main(*args)`.
779+
780+
Args:
781+
None: Uses `sys.argv` instead.
782+
783+
Returns:
784+
int: the underlying exit code int
785+
786+
Minimal Acceptance Testing:
787+
788+
First set up test fixtures by importing multicast.
789+
790+
>>> import multicast
791+
>>> multicast.__main__.main is not None
792+
True
793+
>>>
794+
795+
Testcase 0: calls to cli should return an int.
796+
A: Test that the call to the `cli` function returns an int 0-3.
797+
798+
>>> tst_argv_args = ['''SAY''', '''--port=1234''', '''--message''', '''is required''']
799+
>>> sys.argv = tst_argv_args # normally arguments are aoutomaticly already in argv
800+
>>> test_code = multicast.__main__.cli()
801+
>>> test_code is not None
802+
True
803+
>>> type(test_code) #doctest: -DONT_ACCEPT_BLANKLINE, +ELLIPSIS
804+
<...int...>
805+
>>> int(test_code) >= int(0)
806+
True
807+
>>> int(test_code) < int(4)
808+
True
809+
>>>
810+
811+
Testcase 1: main should error with usage.
812+
A: Test that the multicast component is initialized.
813+
B: Test that the recv component is initialized.
814+
C: Test that the main(recv) function is initialized.
815+
D: Test that the main(recv) function errors with a usage hint by default.
816+
817+
>>> multicast.__main__.main is not None
818+
True
819+
>>> test_code = multicast.__main__.cli() #doctest: +ELLIPSIS
820+
usage: multicast [-h | -V] [--use-std] [--daemon] CMD ...
821+
multicast...
822+
CRITICAL...
823+
>>> type(test_code) #doctest: -DONT_ACCEPT_BLANKLINE, +ELLIPSIS
824+
<...int...>
825+
>>> int(test_code) >= int(0)
826+
True
827+
>>> int(test_code) < int(4)
828+
True
829+
>>>
830+
"""
831+
__EXIT_CODE: tuple = (1, exceptions.EXIT_CODES[1][1])
757832
if (sys.argv is not None) and (len(sys.argv) > 1):
758833
__EXIT_CODE = main(sys.argv[1:])
759834
elif (sys.argv is not None):
760835
__EXIT_CODE = main([__name__, "-h"])
761-
exit(__EXIT_CODE[0]) # skipcq: PYL-R1722 - intentionally allow overwriteing exit for testing
836+
return __EXIT_CODE[0]
837+
838+
839+
if __name__ in '__main__':
840+
exit(cli()) # skipcq: PYL-R1722 - intentionally allow overwriteing exit for testing

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ license-files = ["LICENSE.md"]
1111
maintainers = [{ "name" = "reactive-firewall", "email" = "[email protected]" }]
1212
name = "multicast"
1313
requires-python = ">=3.9.6, !=3.9.7, !=3.9.8, !=3.9.8, !=3.9.10, !=3.9.11, !=3.9.12, !=3.9.13, !=3.9.14, !=3.9.15, !=3.9.16, !=3.9.17, !=3.9.18, !=3.9.19, !=3.13.0, <3.14.0"
14-
scripts = { "multicast" = "multicast.__main__" }
15-
version = "v2.0.9a3"
14+
scripts = { "multicast" = "multicast.__main__:cli" }
15+
version = "v2.0.9a4"
1616

1717
[project.urls]
1818
"Bug Tracker" = "https://github.com/reactive-firewall/multicast/issues"

0 commit comments

Comments
 (0)