Skip to content

Commit b202d53

Browse files
committed
Move some code core.py to new modules
1 parent 08fbc16 commit b202d53

5 files changed

Lines changed: 148 additions & 131 deletions

File tree

src/replhelper/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
from .core import customized_ipython, ipython_options, revise
1+
from .entrypoints import customized_ipython, ipython_options
2+
from .core import revise

src/replhelper/convenience.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import functools
2+
import sys
3+
import warnings
4+
5+
6+
instruction_template = """
7+
8+
Python package "{package}" cannot be imported from Python interpreter
9+
{python}.
10+
{additional_message}
11+
Use your favorite method to install "{need_install}" or run the following
12+
command in Julia (which *tries* to the right thing):
13+
14+
IPython.install_dependency("{need_install}")
15+
16+
It prints the installation command to be executed and prompts your
17+
input (yes/no) before really executing it.
18+
"""
19+
20+
ipython_dependency_missing = """
21+
IPython (version: {IPython.__version__}) is importable but {dependency}
22+
cannot be imported. It is very strange and I'm not sure what is the
23+
best instruction here. Updating IPython could help.
24+
"""
25+
26+
27+
def make_instruction(package, need_install=None, **kwargs):
28+
return instruction_template.format(**dict(dict(
29+
package=package,
30+
need_install=need_install or package.lower(),
31+
python=sys.executable,
32+
additional_message='',
33+
), **kwargs))
34+
35+
36+
def make_dependency_missing_instruction(IPython, dependency):
37+
return make_instruction(
38+
dependency,
39+
need_install='ipython',
40+
additional_message=ipython_dependency_missing.format(
41+
IPython=IPython,
42+
dependency=dependency,
43+
))
44+
45+
46+
def package_name(err):
47+
try:
48+
return err.name
49+
except AttributeError:
50+
# Python 2 support:
51+
prefix = 'No module named '
52+
message = str(err)
53+
if message.startswith(prefix):
54+
return message[len(prefix):].rstrip()
55+
raise ValueError('Cannot determine missing package for error {}'
56+
.format(err))
57+
58+
59+
def print_instruction_on_import_error(f):
60+
@functools.wraps(f)
61+
def wrapped(*args, **kwargs):
62+
try:
63+
return f(*args, **kwargs)
64+
except ImportError as err:
65+
name = package_name(err)
66+
if name in ('IPython', 'julia'):
67+
print(make_instruction(name))
68+
return
69+
if name == 'traitlets':
70+
try:
71+
import IPython
72+
except ImportError:
73+
print(make_instruction('IPython'))
74+
return
75+
print(make_dependency_missing_instruction(IPython, name))
76+
return
77+
raise
78+
return wrapped
79+
80+
81+
segfault_warning = """\
82+
Segmentation fault warning.
83+
84+
You are using IPython version {IPython.__version__} which is known to
85+
cause segmentation fault with tab completion. For segfault-free
86+
IPython, upgrade to version 7 or above (which may still be in
87+
development stage depending on the time you read this message).
88+
Note also that IPython releases after 5.x do not support Python 2.
89+
90+
If you need to install development version of IPython and understand
91+
what would happen to your Python environment by doing so, executing
92+
the following command in Julia may help:
93+
94+
IPython.install_dependency("ipython-dev")
95+
96+
It prints the installation command to be executed and prompts your
97+
input (yes/no) before really executing it.
98+
"""
99+
100+
segfault_warned = False
101+
102+
103+
def maybe_warn_segfault():
104+
global segfault_warned
105+
import IPython
106+
if int(IPython.__version__.split('.', 1)[0]) < 7 and not segfault_warned:
107+
warnings.warn(segfault_warning.format(**vars()))
108+
segfault_warned = True
109+
110+
111+
def with_message(func):
112+
@functools.wraps(func)
113+
def wrapper(*args, **kwds):
114+
maybe_warn_segfault()
115+
return func(*args, **kwds)
116+
return print_instruction_on_import_error(wrapper)

src/replhelper/core.py

Lines changed: 0 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import sys
77
import types
8-
import warnings
98

109
try:
1110
from importlib import reload
@@ -78,80 +77,6 @@ def __dir__(self):
7877
# well in Python REPLs like IPython.
7978

8079

81-
instruction_template = """
82-
83-
Python package "{package}" cannot be imported from Python interpreter
84-
{python}.
85-
{additional_message}
86-
Use your favorite method to install "{need_install}" or run the following
87-
command in Julia (which *tries* to the right thing):
88-
89-
IPython.install_dependency("{need_install}")
90-
91-
It prints the installation command to be executed and prompts your
92-
input (yes/no) before really executing it.
93-
"""
94-
95-
ipython_dependency_missing = """
96-
IPython (version: {IPython.__version__}) is importable but {dependency}
97-
cannot be imported. It is very strange and I'm not sure what is the
98-
best instruction here. Updating IPython could help.
99-
"""
100-
101-
102-
def make_instruction(package, need_install=None, **kwargs):
103-
return instruction_template.format(**dict(dict(
104-
package=package,
105-
need_install=need_install or package.lower(),
106-
python=sys.executable,
107-
additional_message='',
108-
), **kwargs))
109-
110-
111-
def make_dependency_missing_instruction(IPython, dependency):
112-
return make_instruction(
113-
dependency,
114-
need_install='ipython',
115-
additional_message=ipython_dependency_missing.format(
116-
IPython=IPython,
117-
dependency=dependency,
118-
))
119-
120-
121-
def package_name(err):
122-
try:
123-
return err.name
124-
except AttributeError:
125-
# Python 2 support:
126-
prefix = 'No module named '
127-
message = str(err)
128-
if message.startswith(prefix):
129-
return message[len(prefix):].rstrip()
130-
raise ValueError('Cannot determine missing package for error {}'
131-
.format(err))
132-
133-
134-
def print_instruction_on_import_error(f):
135-
def wrapped(*args, **kwargs):
136-
try:
137-
return f(*args, **kwargs)
138-
except ImportError as err:
139-
name = package_name(err)
140-
if name in ('IPython', 'julia'):
141-
print(make_instruction(name))
142-
return
143-
if name == 'traitlets':
144-
try:
145-
import IPython
146-
except ImportError:
147-
print(make_instruction('IPython'))
148-
return
149-
print(make_dependency_missing_instruction(IPython, name))
150-
return
151-
raise
152-
return wrapped
153-
154-
15580
def get_api(main):
15681
if main is None:
15782
return None
@@ -175,60 +100,6 @@ def get_main(**kwargs):
175100
return _Main
176101

177102

178-
def ipython_options(**kwargs):
179-
global _Main
180-
from traitlets.config import Config
181-
182-
_Main = Main = JuliaNameSpace(JuliaAPI(**kwargs))
183-
user_ns = dict(
184-
Main=Main,
185-
)
186-
187-
c = Config()
188-
c.TerminalIPythonApp.display_banner = False
189-
c.TerminalInteractiveShell.confirm_exit = False
190-
191-
from . import ipyext
192-
c.InteractiveShellApp.extensions = [
193-
ipyext.__name__,
194-
]
195-
196-
return dict(user_ns=user_ns, config=c)
197-
198-
199-
segfault_warning = """\
200-
Segmentation fault warning.
201-
202-
You are using IPython version {IPython.__version__} which is known to
203-
cause segmentation fault with tab completion. For segfault-free
204-
IPython, upgrade to version 7 or above (which may still be in
205-
development stage depending on the time you read this message).
206-
Note also that IPython releases after 5.x do not support Python 2.
207-
208-
If you need to install development version of IPython and understand
209-
what would happen to your Python environment by doing so, executing
210-
the following command in Julia may help:
211-
212-
IPython.install_dependency("ipython-dev")
213-
214-
It prints the installation command to be executed and prompts your
215-
input (yes/no) before really executing it.
216-
"""
217-
218-
segfault_warned = False
219-
220-
221-
@print_instruction_on_import_error
222-
def customized_ipython(**kwargs):
223-
global segfault_warned
224-
import IPython
225-
print()
226-
if int(IPython.__version__.split('.', 1)[0]) < 7 and not segfault_warned:
227-
warnings.warn(segfault_warning.format(**vars()))
228-
segfault_warned = True
229-
IPython.start_ipython(**ipython_options(**kwargs))
230-
231-
232103
def revise():
233104
"""Ad-hoc hot reload."""
234105

src/replhelper/entrypoints.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from .convenience import with_message
2+
from .core import get_main
3+
4+
5+
def ipython_options(**kwargs):
6+
from traitlets.config import Config
7+
8+
user_ns = dict(
9+
Main=get_main(**kwargs),
10+
)
11+
12+
c = Config()
13+
c.TerminalIPythonApp.display_banner = False
14+
c.TerminalIPythonApp.matplotlib = None # don't close figures
15+
c.TerminalInteractiveShell.confirm_exit = False
16+
17+
from . import ipyext
18+
c.InteractiveShellApp.extensions = [
19+
ipyext.__name__,
20+
]
21+
22+
return dict(user_ns=user_ns, config=c)
23+
24+
25+
@with_message
26+
def customized_ipython(**kwargs):
27+
import IPython
28+
print()
29+
IPython.start_ipython(**ipython_options(**kwargs))
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
from ..core import print_instruction_on_import_error, package_name, \
3+
from ..convenience import print_instruction_on_import_error, package_name, \
44
make_instruction, make_dependency_missing_instruction
55

66

0 commit comments

Comments
 (0)