Skip to content

Commit bee309b

Browse files
authored
Minor refactoring (#735)
* Added. * Factored out some code. * Added types. * Added imports. * Added types. * Moved after type import.
1 parent 602af7b commit bee309b

File tree

3 files changed

+107
-86
lines changed

3 files changed

+107
-86
lines changed

scalene/find_browser.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
def find_browser():
1+
import webbrowser
2+
from typing import Optional
3+
4+
def find_browser() -> Optional[webbrowser.BaseBrowser]:
25
"""Find the default browser if possible and if compatible."""
3-
import webbrowser
46

57
# Mostly taken from the standard library webbrowser module. Search "console browsers" in there.
68
# In general, a browser belongs on this list of the scalene web GUI doesn't work in it.

scalene/get_module_details.py

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import importlib
2+
import sys
3+
4+
from importlib.abc import SourceLoader
5+
from importlib.machinery import ModuleSpec
6+
from types import CodeType, FrameType
7+
from typing import (
8+
Any,
9+
Callable,
10+
Dict,
11+
List,
12+
Optional,
13+
Set,
14+
Tuple,
15+
Type,
16+
Union,
17+
cast,
18+
)
19+
20+
21+
def _get_module_details(
22+
mod_name: str,
23+
error: Type[Exception] = ImportError,
24+
) -> Tuple[str, ModuleSpec, CodeType]:
25+
"""Copy of `runpy._get_module_details`, but not private."""
26+
if mod_name.startswith("."):
27+
raise error("Relative module names not supported")
28+
pkg_name, _, _ = mod_name.rpartition(".")
29+
if pkg_name:
30+
# Try importing the parent to avoid catching initialization errors
31+
try:
32+
__import__(pkg_name)
33+
except ImportError as e:
34+
# If the parent or higher ancestor package is missing, let the
35+
# error be raised by find_spec() below and then be caught. But do
36+
# not allow other errors to be caught.
37+
if e.name is None or (
38+
e.name != pkg_name and not pkg_name.startswith(e.name + ".")
39+
):
40+
raise
41+
# Warn if the module has already been imported under its normal name
42+
existing = sys.modules.get(mod_name)
43+
if existing is not None and not hasattr(existing, "__path__"):
44+
from warnings import warn
45+
46+
msg = (
47+
"{mod_name!r} found in sys.modules after import of "
48+
"package {pkg_name!r}, but prior to execution of "
49+
"{mod_name!r}; this may result in unpredictable "
50+
"behaviour".format(mod_name=mod_name, pkg_name=pkg_name)
51+
)
52+
warn(RuntimeWarning(msg))
53+
54+
try:
55+
spec = importlib.util.find_spec(mod_name)
56+
except (ImportError, AttributeError, TypeError, ValueError) as ex:
57+
# This hack fixes an impedance mismatch between pkgutil and
58+
# importlib, where the latter raises other errors for cases where
59+
# pkgutil previously raised ImportError
60+
msg = "Error while finding module specification for {!r} ({}: {})"
61+
if mod_name.endswith(".py"):
62+
msg += (
63+
f". Try using '{mod_name[:-3]}' instead of "
64+
f"'{mod_name}' as the module name."
65+
)
66+
raise error(msg.format(mod_name, type(ex).__name__, ex)) from ex
67+
if spec is None:
68+
raise error("No module named %s" % mod_name)
69+
if spec.submodule_search_locations is not None:
70+
if mod_name == "__main__" or mod_name.endswith(".__main__"):
71+
raise error("Cannot use package as __main__ module")
72+
try:
73+
pkg_main_name = mod_name + ".__main__"
74+
return _get_module_details(pkg_main_name, error)
75+
except error as e:
76+
if mod_name not in sys.modules:
77+
raise # No module loaded; being a package is irrelevant
78+
raise error(
79+
("%s; %r is a package and cannot " + "be directly executed")
80+
% (e, mod_name)
81+
)
82+
loader = spec.loader
83+
# use isinstance instead of `is None` to placate mypy
84+
if not isinstance(loader, SourceLoader):
85+
raise error(
86+
"%r is a namespace package and cannot be executed" % mod_name
87+
)
88+
try:
89+
code = loader.get_code(mod_name)
90+
except ImportError as e:
91+
raise error(format(e)) from e
92+
if code is None:
93+
raise error("No code object available for %s" % mod_name)
94+
return mod_name, spec, code
95+

scalene/scalene_profiler.py

+8-84
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,9 @@
4141
# For debugging purposes
4242
from rich.console import Console
4343

44+
from scalene.get_module_details import _get_module_details
4445
from scalene.find_browser import find_browser
4546

46-
console = Console(style="white on blue")
47-
def nada(*args):
48-
pass
49-
# Assigning to `nada` disables any console.log commands.
50-
console.log = nada
51-
5247
from collections import defaultdict
5348
from importlib.abc import SourceLoader
5449
from importlib.machinery import ModuleSpec
@@ -95,6 +90,12 @@ def nada(*args):
9590
from scalene.scalene_parseargs import ScaleneParseArgs, StopJupyterExecution
9691
from scalene.scalene_sigqueue import ScaleneSigQueue
9792

93+
console = Console(style="white on blue")
94+
# Assigning to `nada` disables any console.log commands.
95+
def nada(*args: Any) -> None:
96+
pass
97+
console.log = nada
98+
9899
MINIMUM_PYTHON_VERSION_MAJOR = 3
99100
MINIMUM_PYTHON_VERSION_MINOR = 8
100101

@@ -160,84 +161,7 @@ def start() -> None:
160161
def stop() -> None:
161162
"""Stop profiling."""
162163
Scalene.stop()
163-
164-
165-
def _get_module_details(
166-
mod_name: str,
167-
error: Type[Exception] = ImportError,
168-
) -> Tuple[str, ModuleSpec, CodeType]:
169-
"""Copy of `runpy._get_module_details`, but not private."""
170-
if mod_name.startswith("."):
171-
raise error("Relative module names not supported")
172-
pkg_name, _, _ = mod_name.rpartition(".")
173-
if pkg_name:
174-
# Try importing the parent to avoid catching initialization errors
175-
try:
176-
__import__(pkg_name)
177-
except ImportError as e:
178-
# If the parent or higher ancestor package is missing, let the
179-
# error be raised by find_spec() below and then be caught. But do
180-
# not allow other errors to be caught.
181-
if e.name is None or (
182-
e.name != pkg_name and not pkg_name.startswith(e.name + ".")
183-
):
184-
raise
185-
# Warn if the module has already been imported under its normal name
186-
existing = sys.modules.get(mod_name)
187-
if existing is not None and not hasattr(existing, "__path__"):
188-
from warnings import warn
189-
190-
msg = (
191-
"{mod_name!r} found in sys.modules after import of "
192-
"package {pkg_name!r}, but prior to execution of "
193-
"{mod_name!r}; this may result in unpredictable "
194-
"behaviour".format(mod_name=mod_name, pkg_name=pkg_name)
195-
)
196-
warn(RuntimeWarning(msg))
197-
198-
try:
199-
spec = importlib.util.find_spec(mod_name)
200-
except (ImportError, AttributeError, TypeError, ValueError) as ex:
201-
# This hack fixes an impedance mismatch between pkgutil and
202-
# importlib, where the latter raises other errors for cases where
203-
# pkgutil previously raised ImportError
204-
msg = "Error while finding module specification for {!r} ({}: {})"
205-
if mod_name.endswith(".py"):
206-
msg += (
207-
f". Try using '{mod_name[:-3]}' instead of "
208-
f"'{mod_name}' as the module name."
209-
)
210-
raise error(msg.format(mod_name, type(ex).__name__, ex)) from ex
211-
if spec is None:
212-
raise error("No module named %s" % mod_name)
213-
if spec.submodule_search_locations is not None:
214-
if mod_name == "__main__" or mod_name.endswith(".__main__"):
215-
raise error("Cannot use package as __main__ module")
216-
try:
217-
pkg_main_name = mod_name + ".__main__"
218-
return _get_module_details(pkg_main_name, error)
219-
except error as e:
220-
if mod_name not in sys.modules:
221-
raise # No module loaded; being a package is irrelevant
222-
raise error(
223-
("%s; %r is a package and cannot " + "be directly executed")
224-
% (e, mod_name)
225-
)
226-
loader = spec.loader
227-
# use isinstance instead of `is None` to placate mypy
228-
if not isinstance(loader, SourceLoader):
229-
raise error(
230-
"%r is a namespace package and cannot be executed" % mod_name
231-
)
232-
try:
233-
code = loader.get_code(mod_name)
234-
except ImportError as e:
235-
raise error(format(e)) from e
236-
if code is None:
237-
raise error("No code object available for %s" % mod_name)
238-
return mod_name, spec, code
239-
240-
164+
241165
class Scalene:
242166
"""The Scalene profiler itself."""
243167

0 commit comments

Comments
 (0)