Skip to content

Commit 21eb4ec

Browse files
authored
Merge pull request #18 from timothyhull:timothyhull/issue17
timothyhull/best-practice-refactor-1
2 parents 8eb5e44 + 3eea110 commit 21eb4ec

File tree

5 files changed

+175
-92
lines changed

5 files changed

+175
-92
lines changed

namedtuple_maker/namedtuple_logger.py

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
https://logbook.readthedocs.io/en/stable/
1010
1111
Usage:
12-
# Step 1, import the initialize_logging function into your application:
12+
# Step 1, import the initialize_logging function into your
13+
# application:
1314
from namedtuple_logger import initialize_logging()
1415
initialize_logging()
1516
@@ -23,10 +24,15 @@
2324
app_log.info('Application logging started.')
2425
'''
2526

26-
# Imports
27+
# Imports - Python Standard Library
28+
from os import path
29+
from sys import stdout
30+
31+
# Imports - Third-Party
2732
import logbook
28-
from os import _exit, path
29-
from sys import exit, stderr, stdout
33+
34+
# Imports - Local
35+
from namedtuple_maker.namedtuple_utils import graceful_exit
3036

3137
# Constants
3238
LOG_FILE_PATH = path.curdir
@@ -46,27 +52,6 @@
4652
)
4753

4854

49-
def graceful_exit() -> None:
50-
''' Gracefully exit after a caught exception.
51-
52-
Args:
53-
None.
54-
55-
Returns:
56-
None.
57-
'''
58-
# Graceful exit with status code
59-
try:
60-
61-
# Standard sys.exit
62-
exit(1)
63-
64-
except SystemExit:
65-
66-
# Exit from an interactive REPL shell with os._exit
67-
_exit(1)
68-
69-
7055
def initialize_logging(
7156
log_level: str = None,
7257
log_file: str = LOG_FILE,
@@ -111,8 +96,9 @@ def initialize_logging(
11196
error_message += (f'{index + 1}. {level}\n')
11297

11398
# Display the error message and gracefully exit
114-
print(error_message, file=stderr)
115-
graceful_exit()
99+
graceful_exit(
100+
error_message=error_message
101+
)
116102

117103
else:
118104
# Set logging level
@@ -153,9 +139,10 @@ def initialize_logging(
153139
)
154140

155141
# Display error and gracefully exit
156-
except FileNotFoundError as e:
157-
print(f'\n{e!r}\n', file=stderr)
158-
graceful_exit()
142+
except FileNotFoundError as error:
143+
graceful_exit(
144+
error_object=error,
145+
)
159146

160147
else:
161148

@@ -174,3 +161,5 @@ def initialize_logging(
174161
log_init_log.info(
175162
f'Started {logbook.get_level_name(log_level)} level logging.'
176163
)
164+
165+
return None

namedtuple_maker/namedtuple_maker.py

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -31,24 +31,28 @@ def your_function() -> Iterable:
3131
3232
Logging Target:
3333
The application automatically creates a log file in the current
34-
working directory, and writes log message to that file. To write log
35-
messages to the console (STDOUT), set the LOG_TO_CONSOLE environment
36-
variable to 'True'.
34+
working directory, and writes log message to that file. To
35+
write log messages to the console (STDOUT), set the
36+
LOG_TO_CONSOLE environment variable to 'True'.
3737
3838
Example logging target usage:
3939
export LOG_TO_CONSOLE=True
4040
'''
4141

42-
# Imports
42+
# Imports - Python Standard Library
4343
from collections import namedtuple
4444
from typing import Callable, Iterable, List, NamedTuple
4545
from functools import wraps
46-
from namedtuple_maker.namedtuple_logger import initialize_logging
4746
from re import compile, VERBOSE
48-
from os import _exit, getenv
49-
from sys import exit, stderr
47+
from os import getenv
48+
49+
# Imports - Third-Party
5050
import logbook
5151

52+
# Imports - Local
53+
from namedtuple_maker.namedtuple_logger import initialize_logging
54+
from namedtuple_maker.namedtuple_utils import graceful_exit
55+
5256
# Check for LOG_LEVEL environment variable
5357
log_level = getenv('LOG_LEVEL')
5458

@@ -260,16 +264,17 @@ def convert_to_namedtuple(*args, **kwargs) -> namedtuple:
260264
261265
attribute_names (Iterable[str]):
262266
Optional kwarg, any iterable object class
263-
including, list, tuple, dict_keys, dict_values, etc.
264-
with str values.
267+
including, list, tuple, dict_keys, dict_values,
268+
etc. with str values.
265269
266270
auto_attribute_names (bool):
267271
Optional kwarg, automatically name attributes
268-
without user input or use of the attribute_names
269-
parameter. Default: False
272+
without user input or use of the
273+
attribute_names parameter. Default: False
270274
271275
Returns: named_tuple (namedtuple):
272-
Class NamedTuple instantiated from collections.namedtuple
276+
Class NamedTuple instantiated from
277+
collections.namedtuple
273278
'''
274279

275280
# Log entry
@@ -587,8 +592,8 @@ def make_named_tuple(
587592
Any iterable object to convert to a namedtuple.
588593
589594
attribute_names (Iterable[str]):
590-
Any iterable object of strings to supply field names for
591-
a namedtuple.
595+
Any iterable object of strings to supply field names
596+
for a namedtuple.
592597
593598
auto_attribute_names (bool):
594599
Optional kwarg, automatically name attributes
@@ -640,7 +645,7 @@ def make_named_tuple(
640645
f'\t{iterable_input}'
641646
)
642647

643-
except TypeError as e:
648+
except TypeError as error:
644649

645650
# Set error message value
646651
error_message = (
@@ -655,22 +660,13 @@ def make_named_tuple(
655660
f'\t"iterable_input" value: {iterable_input}'
656661
f'\t"iterable_input" type: {type(iterable_input)}'
657662
)
658-
application_log.exception(e)
659-
660-
# Write error message to STDERR
661-
print(f'\n{error_message}')
662-
print(f'{e!r}\n', file=stderr)
663+
application_log.exception(error)
663664

664-
# Graceful exit with status code
665-
try:
666-
667-
# Standard sys.exit
668-
exit(1)
669-
670-
except SystemExit:
671-
672-
# Exit from an interactive REPL shell with os._exit
673-
_exit(1)
665+
# Display the error message and gracefully exit
666+
graceful_exit(
667+
error_message=error_message,
668+
error_object=error
669+
)
674670

675671
# Log Entry
676672
# Log return of make_named_tuple function to named_tuple_converter
@@ -696,8 +692,9 @@ def run_make_named_tuple() -> NamedTuple:
696692
697693
Returns:
698694
named_tuple (NamedTuple):
699-
NamedTuple class object resulting from the make_named_tuple
700-
function decorated by the named_tuple_converter function.
695+
NamedTuple class object resulting from the
696+
make_named_tuple function decorated by the
697+
named_tuple_converter function.
701698
'''
702699

703700
# Log Entry

namedtuple_maker/namedtuple_utils.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/usr/bin/env python3
2+
''' Utility module with functions for the namedtuple-maker application.
3+
4+
Usage:
5+
See individual function docstrings for usage instructions.
6+
'''
7+
8+
# Imports - Python Standard Library
9+
from os import _exit
10+
from sys import exit, stderr
11+
from typing import AnyStr
12+
13+
14+
def graceful_exit(
15+
error_message: AnyStr = None,
16+
error_object: Exception = None
17+
):
18+
''' Gracefully exit a program after catching an exception. Call the
19+
graceful_exit function from within a higher-level except block.
20+
Instead of using the raise keyword to display a stack trace and
21+
exit, graceful_exit will display a friendly message, and exit the
22+
program without displaying full trace stack details.
23+
24+
The graceful_exit function first attempts to use the sys.exit
25+
function to exit the application. If sys.exit raises a
26+
SystemExit exception, usually as the result of running a program
27+
within an interactive shell (IDLE, iPython, etc.), graceful_exit
28+
will use the os._exit function.
29+
30+
Args:
31+
error_message (AnyStr, optional):
32+
String error message to display.
33+
34+
error_object (Exception):
35+
Exception object from the source except block. Write
36+
the output to STDERR.
37+
38+
Usage:
39+
# Step 1, import the graceful_exit() function into your
40+
# application.
41+
from graceful_exit import graceful exit.
42+
43+
# Step 2, define a try/except block to test your code for
44+
# exceptions.
45+
try:
46+
my_function()
47+
except NameError:
48+
# TBD
49+
50+
# Step 3, call the graceful_exit function within the except
51+
# block
52+
try:
53+
my_function()
54+
except NameError as error:
55+
graceful_exit(
56+
error_message='An error occurred',
57+
error_object=error
58+
)
59+
60+
Returns:
61+
N/A.
62+
'''
63+
64+
# Display the optional error message
65+
if error_message is not None:
66+
print(f'\n{error_message}')
67+
68+
# Write the error_object to STDERR, use the shorthand for repr(error)
69+
if error_object is not None:
70+
print(f'{error_object!r}\n', file=stderr)
71+
72+
# Graceful exit with status code
73+
try:
74+
75+
# Standard sys.exit
76+
exit(1)
77+
78+
except SystemExit:
79+
80+
# Exit from an interactive REPL shell with os._exit
81+
_exit(1)

tests/test_namedtuple_logger.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
1414
'''
1515

16-
# Imports
17-
from namedtuple_maker.namedtuple_logger import initialize_logging
16+
# Imports - Third-Party
1817
from logbook import Logger
1918
# from pytest import raises
2019

20+
# Imports - Local
21+
from namedtuple_maker.namedtuple_logger import initialize_logging
22+
2123
# Constants
2224
LOG_FILE_INVALID = './bad_log_test_dir/log_file.log'
2325
LOG_INFO_MESSAGE = 'This is a log entry.'
@@ -27,9 +29,9 @@
2729

2830

2931
def test_initialize_logging_to_console(capfd) -> None:
30-
''' Test initialize_logging function for console output. Writes mock
31-
log messages to the console and verifies the log messages display
32-
correctly.
32+
''' Test initialize_logging function for console output. Writes
33+
mock log messages to the console and verifies the log messages
34+
display correctly.
3335
3436
Args:
3537
capfd (pytest fixture):
@@ -56,6 +58,8 @@ def test_initialize_logging_to_console(capfd) -> None:
5658
log_output = capfd.readouterr().out
5759
assert LOG_INFO_MESSAGE in log_output
5860

61+
return None
62+
5963

6064
# def test_initialize_logging_invalid_log_file() -> None:
6165
# ''' Test initialize logging function's ability to handle an invalid
@@ -72,3 +76,4 @@ def test_initialize_logging_to_console(capfd) -> None:
7276
# initialize_logging(
7377
# log_file=LOG_FILE_INVALID
7478
# )
79+
# return None

0 commit comments

Comments
 (0)