Skip to content

Commit cade8cb

Browse files
committed
Replace exit with return for python API use
1 parent 4dd4ed4 commit cade8cb

File tree

3 files changed

+55
-46
lines changed

3 files changed

+55
-46
lines changed

bibtexautocomplete/core/main.py

Lines changed: 51 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from pathlib import Path
33
from sys import stdout
44
from tempfile import mkstemp
5-
from typing import Any, Callable, Container, List, NoReturn, Optional, Set
5+
from typing import Any, Callable, Container, List, Optional, Set
66

77
from bibtexparser.bibdatabase import UndefinedString
88

@@ -42,31 +42,37 @@
4242
pass
4343

4444

45-
def conflict(parser: MyParser, prefix: str, option1: str, option2: str) -> NoReturn:
46-
parser.error(
47-
"{StBold}Conflicting options:\n{Reset}"
48-
+ " Specified both "
49-
+ prefix
50-
+ "{FgYellow}"
51-
+ option1
52-
+ "{Reset} and a {FgYellow}"
53-
+ option2
54-
+ "{Reset} option."
55-
)
45+
def conflict(parser: MyParser, prefix: str, option1: str, option2: str) -> int:
46+
try:
47+
parser.error(
48+
"{StBold}Conflicting options:\n{Reset}"
49+
+ " Specified both "
50+
+ prefix
51+
+ "{FgYellow}"
52+
+ option1
53+
+ "{Reset} and a {FgYellow}"
54+
+ option2
55+
+ "{Reset} option."
56+
)
57+
except ValueError:
58+
return 2
5659

5760

58-
def main(argv: Optional[List[str]] = None) -> None:
61+
def main(argv: Optional[List[str]] = None) -> int:
5962
"""The main function of bibtexautocomplete
6063
Takes an argv like List as argument,
6164
if none, uses sys.argv
6265
see HELP_TEXT or main(["-h"]) for details"""
6366
parser = make_parser()
6467
if parser_autocomplete is not None:
6568
parser_autocomplete(parser)
66-
if argv is None:
67-
args = parser.parse_args()
68-
else:
69-
args = parser.parse_args(argv)
69+
try:
70+
if argv is None:
71+
args = parser.parse_args()
72+
else:
73+
args = parser.parse_args(argv)
74+
except ValueError:
75+
return 2
7076

7177
ANSICodes.use_ansi = stdout.isatty() and not args.no_color
7278

@@ -85,14 +91,14 @@ def main(argv: Optional[List[str]] = None) -> None:
8591
PREFIX=FIELD_PREFIX,
8692
)
8793
)
88-
return
94+
return 0
8995
if args.version:
9096
print(
9197
"{NAME} version {VERSION} ({VERSION_DATE})".format(
9298
NAME=SCRIPT_NAME, VERSION=VERSION_STR, VERSION_DATE=VERSION_DATE
9399
)
94100
)
95-
return
101+
return 0
96102

97103
if args.silent:
98104
args.verbose = -args.silent
@@ -111,7 +117,7 @@ def main(argv: Optional[List[str]] = None) -> None:
111117

112118
lookups = OnlyExclude[str].from_nonempty(args.only_query, args.dont_query).filter(LOOKUPS, lambda x: x.name)
113119
if args.only_query != [] and args.dont_query != []:
114-
conflict(parser, "a ", "-q/--only-query", "-Q/--dont-query")
120+
return conflict(parser, "a ", "-q/--only-query", "-Q/--dont-query")
115121
if args.only_query != []:
116122
# remove duplicate from list
117123
args.only_query, dups = list_unduplicate(args.only_query)
@@ -122,11 +128,11 @@ def main(argv: Optional[List[str]] = None) -> None:
122128

123129
fields = OnlyExclude[FieldType].from_nonempty(args.only_complete, args.dont_complete)
124130
if args.only_complete != [] and args.dont_complete != []:
125-
conflict(parser, "a ", "-c/--only-complete", "-C/--dont-complete")
131+
return conflict(parser, "a ", "-c/--only-complete", "-C/--dont-complete")
126132

127133
entries = OnlyExclude[str].from_nonempty(args.only_entry, args.exclude_entry)
128134
if args.only_entry != [] and args.exclude_entry != []:
129-
conflict(parser, "a ", "-e/--only-entry", "-E/--exclude-entry")
135+
return conflict(parser, "a ", "-e/--only-entry", "-E/--exclude-entry")
130136

131137
if args.protect_all_uppercase:
132138
fields_to_protect_uppercase: Container[str] = FieldNamesSet
@@ -135,11 +141,11 @@ def main(argv: Optional[List[str]] = None) -> None:
135141
fields_to_protect_proto.default = False
136142
fields_to_protect_uppercase = fields_to_protect_proto
137143
if args.protect_all_uppercase and args.protect_uppercase != []:
138-
conflict(parser, "", "--fpa/--protect-all-uppercase", "--fp/--protect-uppercase")
144+
return conflict(parser, "", "--fpa/--protect-all-uppercase", "--fp/--protect-uppercase")
139145
if args.protect_all_uppercase and args.dont_protect_uppercase != []:
140-
conflict(parser, "", "--fpa/--protect-all-uppercase", "--FP/--dont-protect-uppercase")
146+
return conflict(parser, "", "--fpa/--protect-all-uppercase", "--FP/--dont-protect-uppercase")
141147
if args.protect_uppercase != [] and args.dont_protect_uppercase != []:
142-
conflict(parser, "a ", "--fp/--protect-uppercase", "--FP/--dont-protect-uppercase")
148+
return conflict(parser, "a ", "--fp/--protect-uppercase", "--FP/--dont-protect-uppercase")
143149

144150
if args.force_overwrite:
145151
fields_to_overwrite: Set[FieldType] = FieldNamesSet
@@ -148,20 +154,23 @@ def main(argv: Optional[List[str]] = None) -> None:
148154
overwrite.default = False
149155
fields_to_overwrite = set(overwrite.filter(FieldNamesSet, lambda x: x))
150156
if args.force_overwrite and args.overwrite != []:
151-
conflict(parser, "", "-f/--force-overwrite", "-w/--overwrite")
157+
return conflict(parser, "", "-f/--force-overwrite", "-w/--overwrite")
152158
if args.force_overwrite and args.dont_overwrite != []:
153-
conflict(parser, "", "-f/--force-overwrite", "-W/--dont-overwrite")
159+
return conflict(parser, "", "-f/--force-overwrite", "-W/--dont-overwrite")
154160
if args.overwrite != [] and args.dont_overwrite != []:
155-
conflict(parser, "a ", "-w/--overwrite", "-W/--dont-overwrite")
161+
return conflict(parser, "a ", "-w/--overwrite", "-W/--dont-overwrite")
156162

157163
if args.diff and args.inplace:
158-
parser.error(
159-
"Cannot use {FgYellow}-D/--diff{Reset} flag and {FgYellow}-i/--inplace{Reset} flag "
160-
"simultaneously, as there\n"
161-
" is a big risk of deleting data.\n"
162-
" If that is truly what you want to do, specify the output file explictly\n"
163-
" with {FgYellow}-o / --output {FgGreen}<filename>{Reset}."
164-
)
164+
try:
165+
parser.error(
166+
"Cannot use {FgYellow}-D/--diff{Reset} flag and {FgYellow}-i/--inplace{Reset} flag "
167+
"simultaneously, as there\n"
168+
" is a big risk of deleting data.\n"
169+
" If that is truly what you want to do, specify the output file explictly\n"
170+
" with {FgYellow}-o / --output {FgGreen}<filename>{Reset}."
171+
)
172+
except ValueError:
173+
return 2
165174

166175
try:
167176
completer = BibtexAutocomplete(
@@ -200,7 +209,7 @@ def main(argv: Optional[List[str]] = None) -> None:
200209
logger.warn("Interrupted")
201210
if completer.position == 0:
202211
logger.info("No entries were completed")
203-
return None
212+
return 5
204213
_, tempfile = mkstemp(suffix=".btac.bib", prefix="btac-interrupt-", text=True)
205214
logger.header("Dumping data")
206215
with open(tempfile, "w") as file:
@@ -226,12 +235,14 @@ def main(argv: Optional[List[str]] = None) -> None:
226235
if i == completer.position:
227236
logger.info("Only completed entries up to and including '{}'.\n".format(entry.get("ID", "<no_id>")))
228237
break_next = True
229-
238+
return 5
230239
except KeyboardInterrupt:
231240
logger.warn("Interrupted x2")
241+
return 7
232242
except ValueError:
233-
exit(2)
243+
return 2
234244
except UndefinedString:
235-
exit(1)
245+
return 1
236246
except (IOError, UnicodeDecodeError):
237-
exit(1)
247+
return 1
248+
return 0

bibtexautocomplete/core/parser.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from typing import IO, Iterable, List, NoReturn, Optional, TypeVar
1111

1212
from ..bibtex.constants import FieldNamesSet
13+
from ..utils.ansi import ANSICodes
1314
from ..utils.constants import BTAC_FILENAME, CONNECTION_TIMEOUT, SCRIPT_NAME
1415
from ..utils.logger import logger
1516
from .apis import LOOKUP_NAMES
@@ -116,7 +117,7 @@ def print_usage(self, file: Optional[IO[str]] = None) -> None:
116117
def error(self, message: str) -> NoReturn:
117118
logger.critical(message + "\n", error="Invalid command line", NAME=SCRIPT_NAME)
118119
self.print_usage(stderr)
119-
raise ValueError(message)
120+
raise ValueError(message.format(**ANSICodes.EmptyCodes))
120121

121122

122123
def make_parser() -> MyParser:

tests/test_6_main.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ def get_value(self, res: SafeJSON) -> BibtexEntry:
546546

547547
@pytest.mark.parametrize(("argv", "files_to_compare"), tests)
548548
def test_main(argv: List[str], files_to_compare: List[Tuple[str, str]]) -> None:
549-
main(argv)
549+
assert main(argv) == 0
550550
FakeLookup.count = 0
551551
day = datetime.today().strftime("%Y-%m-%d")
552552
for expected, generated in files_to_compare:
@@ -637,10 +637,7 @@ def test_main(argv: List[str], files_to_compare: List[Tuple[str, str]]) -> None:
637637

638638
@pytest.mark.parametrize(("argv", "exit_code"), exit_tests)
639639
def test_main_exit(argv: List[str], exit_code: int) -> None:
640-
with pytest.raises(SystemExit) as test_exit:
641-
main(argv)
642-
assert test_exit.type is SystemExit
643-
assert test_exit.value.code == exit_code
640+
assert main(argv) == exit_code
644641

645642

646643
def test_promote() -> None:

0 commit comments

Comments
 (0)