Skip to content

Commit 0ea775f

Browse files
authored
attempt to improve import time (#50)
* WIP attempt to improve import time * improving speedups * tweak imports * fix coverage * fix coverage upload * fix ci coverage
1 parent 8feb278 commit 0ea775f

File tree

9 files changed

+146
-82
lines changed

9 files changed

+146
-82
lines changed

.codecov.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
coverage:
2+
precision: 2
3+
range: [95, 100]
4+
5+
comment:
6+
layout: 'header, diff, flags, files, footer'

.github/workflows/ci.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,11 @@ jobs:
4343
make check-dist
4444
4545
- name: test with extras
46-
run: make test
46+
run: |
47+
make test
48+
coverage xml
4749
48-
- uses: codecov/codecov-action@v1
50+
- uses: codecov/codecov-action@v1.0.7
4951
with:
5052
file: ./coverage.xml
5153
env_vars: EXTRAS,PYTHON,OS
@@ -58,10 +60,10 @@ jobs:
5860
make test
5961
coverage xml
6062
61-
- uses: codecov/codecov-action@v1
63+
- uses: codecov/codecov-action@v1.0.7
6264
with:
6365
file: ./coverage.xml
64-
env_vars: DEPS,PYTHON,OS
66+
env_vars: EXTRAS,PYTHON,OS
6567
env:
6668
EXTRAS: no
6769

devtools/ansi.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,20 @@
1-
import re
2-
import sys
31
from enum import IntEnum
4-
from typing import Any
2+
3+
from .utils import isatty
54

65
_ansi_template = '\033[{}m'
7-
_ansi_re = re.compile('\033\\[((?:\\d|;)*)([a-zA-Z])')
86

97
__all__ = 'sformat', 'sprint'
108

11-
12-
def isatty(stream=None):
13-
stream = stream or sys.stdout
14-
try:
15-
return stream.isatty()
16-
except Exception:
17-
return False
9+
MYPY = False
10+
if MYPY:
11+
from typing import Any
1812

1913

2014
def strip_ansi(value):
21-
return _ansi_re.sub('', value)
15+
import re
16+
17+
return re.sub('\033\\[((?:\\d|;)*)([a-zA-Z])', '', value)
2218

2319

2420
class Style(IntEnum):
@@ -68,7 +64,7 @@ class Style(IntEnum):
6864
# this is a meta value used for the "Style" instance which is the "style" function
6965
function = -1
7066

71-
def __call__(self, input: Any, *styles, reset: bool = True, apply: bool = True):
67+
def __call__(self, input: 'Any', *styles: 'Style', reset: bool = True, apply: bool = True) -> str:
7268
"""
7369
Styles text with ANSI styles and returns the new string.
7470

devtools/debug.py

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
1-
import ast
2-
import dis
3-
import inspect
41
import os
5-
import pdb
62
import sys
7-
from pathlib import Path
8-
from textwrap import dedent, indent
9-
from types import FrameType
10-
from typing import Generator, List, Optional, Tuple
113

12-
from .ansi import isatty, sformat
4+
from .ansi import sformat
135
from .prettier import PrettyFormat, env_true
146
from .timer import Timer
7+
from .utils import isatty
158

169
__all__ = 'Debug', 'debug'
17-
CWD = Path('.').resolve()
10+
MYPY = False
11+
if MYPY:
12+
import ast
13+
from types import FrameType
14+
from typing import Generator, List, Optional, Tuple
1815

1916

2017
pformat = PrettyFormat(
@@ -78,7 +75,7 @@ class DebugOutput:
7875
arg_class = DebugArgument
7976
__slots__ = 'filename', 'lineno', 'frame', 'arguments', 'warning'
8077

81-
def __init__(self, *, filename: str, lineno: int, frame: str, arguments: List[DebugArgument], warning=None):
78+
def __init__(self, *, filename: str, lineno: int, frame: str, arguments: 'List[DebugArgument]', warning=None):
8279
self.filename = filename
8380
self.lineno = lineno
8481
self.frame = frame
@@ -110,22 +107,9 @@ def __repr__(self) -> str:
110107

111108
class Debug:
112109
output_class = DebugOutput
113-
complex_nodes = (
114-
ast.Call,
115-
ast.Attribute,
116-
ast.Subscript,
117-
ast.IfExp,
118-
ast.BoolOp,
119-
ast.BinOp,
120-
ast.Compare,
121-
ast.DictComp,
122-
ast.ListComp,
123-
ast.SetComp,
124-
ast.GeneratorExp,
125-
)
126110

127111
def __init__(
128-
self, *, warnings: Optional[bool] = None, highlight: Optional[bool] = None, frame_context_length: int = 50
112+
self, *, warnings: 'Optional[bool]' = None, highlight: 'Optional[bool]' = None, frame_context_length: int = 50
129113
):
130114
self._show_warnings = self._env_bool(warnings, 'PY_DEVTOOLS_WARNINGS', True)
131115
self._highlight = self._env_bool(highlight, 'PY_DEVTOOLS_HIGHLIGHT', None)
@@ -149,6 +133,8 @@ def format(self, *args, **kwargs) -> DebugOutput:
149133
return self._process(args, kwargs, 'format')
150134

151135
def breakpoint(self):
136+
import pdb
137+
152138
pdb.Pdb(skip=['devtools.*']).set_trace()
153139

154140
def timer(self, name=None, *, verbose=True, file=None, dp=3) -> Timer:
@@ -160,7 +146,7 @@ def _process(self, args, kwargs, func_name: str) -> DebugOutput:
160146
"""
161147
# HELP: any errors other than ValueError from _getframe? If so please submit an issue
162148
try:
163-
call_frame: FrameType = sys._getframe(2)
149+
call_frame: 'FrameType' = sys._getframe(2)
164150
except ValueError:
165151
# "If [ValueError] is deeper than the call stack, ValueError is raised"
166152
return self.output_class(
@@ -175,15 +161,20 @@ def _process(self, args, kwargs, func_name: str) -> DebugOutput:
175161
function = call_frame.f_code.co_name
176162
if filename.startswith('/'):
177163
# make the path relative
164+
from pathlib import Path
165+
166+
cwd = Path('.').resolve()
178167
try:
179-
filename = str(Path(filename).relative_to(CWD))
168+
filename = str(Path(filename).relative_to(cwd))
180169
except ValueError:
181170
# happens if filename path is not within CWD
182171
pass
183172

184173
lineno = call_frame.f_lineno
185174
warning = None
186175

176+
import inspect
177+
187178
try:
188179
file_lines, _ = inspect.findsource(call_frame)
189180
except OSError:
@@ -214,7 +205,23 @@ def _args_inspection_failed(self, args, kwargs):
214205
for name, value in kwargs.items():
215206
yield self.output_class.arg_class(value, name=name)
216207

217-
def _process_args(self, func_ast, code_lines, args, kwargs) -> Generator[DebugArgument, None, None]: # noqa: C901
208+
def _process_args(self, func_ast, code_lines, args, kwargs) -> 'Generator[DebugArgument, None, None]': # noqa: C901
209+
import ast
210+
211+
complex_nodes = (
212+
ast.Call,
213+
ast.Attribute,
214+
ast.Subscript,
215+
ast.IfExp,
216+
ast.BoolOp,
217+
ast.BinOp,
218+
ast.Compare,
219+
ast.DictComp,
220+
ast.ListComp,
221+
ast.SetComp,
222+
ast.GeneratorExp,
223+
)
224+
218225
arg_offsets = list(self._get_offsets(func_ast))
219226
for i, arg in enumerate(args):
220227
try:
@@ -226,7 +233,7 @@ def _process_args(self, func_ast, code_lines, args, kwargs) -> Generator[DebugAr
226233

227234
if isinstance(ast_node, ast.Name):
228235
yield self.output_class.arg_class(arg, name=ast_node.id)
229-
elif isinstance(ast_node, self.complex_nodes):
236+
elif isinstance(ast_node, complex_nodes):
230237
# TODO replace this hack with astor when it get's round to a new release
231238
start_line, start_col = arg_offsets[i]
232239

@@ -252,12 +259,14 @@ def _process_args(self, func_ast, code_lines, args, kwargs) -> Generator[DebugAr
252259
yield self.output_class.arg_class(value, name=name, variable=kw_arg_names.get(name))
253260

254261
def _parse_code(
255-
self, filename: str, file_lines: List[str], first_line: int, last_line: int
256-
) -> Tuple[ast.AST, List[str]]:
262+
self, filename: str, file_lines: 'List[str]', first_line: int, last_line: int
263+
) -> 'Tuple[ast.AST, List[str]]':
257264
"""
258265
All we're trying to do here is build an AST of the function call statement. However numerous ugly interfaces,
259266
lack on introspection support and changes between python versions make this extremely hard.
260267
"""
268+
from textwrap import dedent
269+
import ast
261270

262271
def get_code(_last_line: int) -> str:
263272
lines = file_lines[first_line - 1 : _last_line]
@@ -295,10 +304,12 @@ def get_code(_last_line: int) -> str:
295304
return func_ast, code_lines
296305

297306
@staticmethod # noqa: C901
298-
def _statement_range(call_frame: FrameType, func_name: str) -> Tuple[int, int]: # noqa: C901
307+
def _statement_range(call_frame: 'FrameType', func_name: str) -> 'Tuple[int, int]': # noqa: C901
299308
"""
300309
Try to find the start and end of a frame statement.
301310
"""
311+
import dis
312+
302313
# dis.disassemble(call_frame.f_code, call_frame.f_lasti)
303314
# pprint([i for i in dis.get_instructions(call_frame.f_code)])
304315

@@ -343,15 +354,20 @@ def _statement_range(call_frame: FrameType, func_name: str) -> Tuple[int, int]:
343354
return first_line, last_line
344355

345356
@staticmethod
346-
def _wrap_parse(code, filename):
357+
def _wrap_parse(code: str, filename: str) -> 'ast.Call':
347358
"""
348359
async wrapper is required to avoid await calls raising a SyntaxError
349360
"""
361+
import ast
362+
from textwrap import indent
363+
350364
code = 'async def wrapper():\n' + indent(code, ' ')
351365
return ast.parse(code, filename=filename).body[0].body[0].value
352366

353367
@staticmethod
354368
def _get_offsets(func_ast):
369+
import ast
370+
355371
for arg in func_ast.args:
356372
start_line, start_col = arg.lineno - 2, arg.col_offset - 1
357373

0 commit comments

Comments
 (0)