Show More
@@ -0,0 +1,11 b'' | |||||
|
1 | The default tracebackmode will now skip frames that are marked with | |||
|
2 | ``__tracebackhide__ = True`` and show how many traceback frames have been | |||
|
3 | skipped. This can be toggled by using :magic:`xmode` with the ``--show`` or | |||
|
4 | ``--hide`` attribute. It will have no effect on non verbose traceback modes. | |||
|
5 | ||||
|
6 | The ipython debugger also now understand ``__tracebackhide__`` as well and will | |||
|
7 | skip hidden frames when displaying. Movement up and down the stack will skip the | |||
|
8 | hidden frames and will show how many frames were hidden. Internal IPython frames | |||
|
9 | are also now hidden by default. The behavior can be changed with the | |||
|
10 | ``skip_hidden`` command and accepts "yes", "no", "true" and "false" case | |||
|
11 | insensitive parameters. |
@@ -280,26 +280,31 b' class Pdb(OldPdb):' | |||||
280 |
|
280 | |||
281 | # Set the prompt - the default prompt is '(Pdb)' |
|
281 | # Set the prompt - the default prompt is '(Pdb)' | |
282 | self.prompt = prompt |
|
282 | self.prompt = prompt | |
|
283 | self.skip_hidden = True | |||
283 |
|
284 | |||
284 | def set_colors(self, scheme): |
|
285 | def set_colors(self, scheme): | |
285 | """Shorthand access to the color table scheme selector method.""" |
|
286 | """Shorthand access to the color table scheme selector method.""" | |
286 | self.color_scheme_table.set_active_scheme(scheme) |
|
287 | self.color_scheme_table.set_active_scheme(scheme) | |
287 | self.parser.style = scheme |
|
288 | self.parser.style = scheme | |
288 |
|
289 | |||
|
290 | ||||
|
291 | def hidden_frames(self, stack): | |||
|
292 | """ | |||
|
293 | Given an index in the stack return wether it should be skipped. | |||
|
294 | ||||
|
295 | This is used in up/down and where to skip frames. | |||
|
296 | """ | |||
|
297 | ip_hide = [s[0].f_locals.get("__tracebackhide__", False) for s in stack] | |||
|
298 | ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"] | |||
|
299 | if ip_start: | |||
|
300 | ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)] | |||
|
301 | return ip_hide | |||
|
302 | ||||
289 | def interaction(self, frame, traceback): |
|
303 | def interaction(self, frame, traceback): | |
290 | try: |
|
304 | try: | |
291 | OldPdb.interaction(self, frame, traceback) |
|
305 | OldPdb.interaction(self, frame, traceback) | |
292 | except KeyboardInterrupt: |
|
306 | except KeyboardInterrupt: | |
293 |
self.stdout.write( |
|
307 | self.stdout.write("\n" + self.shell.get_exception_only()) | |
294 |
|
||||
295 | def new_do_up(self, arg): |
|
|||
296 | OldPdb.do_up(self, arg) |
|
|||
297 | do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up) |
|
|||
298 |
|
||||
299 | def new_do_down(self, arg): |
|
|||
300 | OldPdb.do_down(self, arg) |
|
|||
301 |
|
||||
302 | do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down) |
|
|||
303 |
|
308 | |||
304 | def new_do_frame(self, arg): |
|
309 | def new_do_frame(self, arg): | |
305 | OldPdb.do_frame(self, arg) |
|
310 | OldPdb.do_frame(self, arg) | |
@@ -320,6 +325,8 b' class Pdb(OldPdb):' | |||||
320 | return self.do_quit(arg) |
|
325 | return self.do_quit(arg) | |
321 |
|
326 | |||
322 | def print_stack_trace(self, context=None): |
|
327 | def print_stack_trace(self, context=None): | |
|
328 | Colors = self.color_scheme_table.active_colors | |||
|
329 | ColorsNormal = Colors.Normal | |||
323 | if context is None: |
|
330 | if context is None: | |
324 | context = self.context |
|
331 | context = self.context | |
325 | try: |
|
332 | try: | |
@@ -329,8 +336,21 b' class Pdb(OldPdb):' | |||||
329 | except (TypeError, ValueError): |
|
336 | except (TypeError, ValueError): | |
330 | raise ValueError("Context must be a positive integer") |
|
337 | raise ValueError("Context must be a positive integer") | |
331 | try: |
|
338 | try: | |
332 | for frame_lineno in self.stack: |
|
339 | skipped = 0 | |
|
340 | for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack): | |||
|
341 | if hidden and self.skip_hidden: | |||
|
342 | skipped += 1 | |||
|
343 | continue | |||
|
344 | if skipped: | |||
|
345 | print( | |||
|
346 | f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n" | |||
|
347 | ) | |||
|
348 | skipped = 0 | |||
333 | self.print_stack_entry(frame_lineno, context=context) |
|
349 | self.print_stack_entry(frame_lineno, context=context) | |
|
350 | if skipped: | |||
|
351 | print( | |||
|
352 | f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n" | |||
|
353 | ) | |||
334 | except KeyboardInterrupt: |
|
354 | except KeyboardInterrupt: | |
335 | pass |
|
355 | pass | |
336 |
|
356 | |||
@@ -487,6 +507,16 b' class Pdb(OldPdb):' | |||||
487 | except KeyboardInterrupt: |
|
507 | except KeyboardInterrupt: | |
488 | pass |
|
508 | pass | |
489 |
|
509 | |||
|
510 | def do_skip_hidden(self, arg): | |||
|
511 | """ | |||
|
512 | Change whether or not we should skip frames with the | |||
|
513 | __tracebackhide__ attribute. | |||
|
514 | """ | |||
|
515 | if arg.strip().lower() in ("true", "yes"): | |||
|
516 | self.skip_hidden = True | |||
|
517 | elif arg.strip().lower() in ("false", "no"): | |||
|
518 | self.skip_hidden = False | |||
|
519 | ||||
490 | def do_list(self, arg): |
|
520 | def do_list(self, arg): | |
491 | """Print lines of code from the current stack frame |
|
521 | """Print lines of code from the current stack frame | |
492 | """ |
|
522 | """ | |
@@ -633,6 +663,109 b' class Pdb(OldPdb):' | |||||
633 |
|
663 | |||
634 | do_w = do_where |
|
664 | do_w = do_where | |
635 |
|
665 | |||
|
666 | def stop_here(self, frame): | |||
|
667 | hidden = False | |||
|
668 | if self.skip_hidden: | |||
|
669 | hidden = frame.f_locals.get("__tracebackhide__", False) | |||
|
670 | if hidden: | |||
|
671 | Colors = self.color_scheme_table.active_colors | |||
|
672 | ColorsNormal = Colors.Normal | |||
|
673 | print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n") | |||
|
674 | ||||
|
675 | return super().stop_here(frame) | |||
|
676 | ||||
|
677 | def do_up(self, arg): | |||
|
678 | """u(p) [count] | |||
|
679 | Move the current frame count (default one) levels up in the | |||
|
680 | stack trace (to an older frame). | |||
|
681 | ||||
|
682 | Will skip hidden frames. | |||
|
683 | """ | |||
|
684 | ## modified version of upstream that skips | |||
|
685 | # frames with __tracebackide__ | |||
|
686 | if self.curindex == 0: | |||
|
687 | self.error("Oldest frame") | |||
|
688 | return | |||
|
689 | try: | |||
|
690 | count = int(arg or 1) | |||
|
691 | except ValueError: | |||
|
692 | self.error("Invalid frame count (%s)" % arg) | |||
|
693 | return | |||
|
694 | skipped = 0 | |||
|
695 | if count < 0: | |||
|
696 | _newframe = 0 | |||
|
697 | else: | |||
|
698 | _newindex = self.curindex | |||
|
699 | counter = 0 | |||
|
700 | hidden_frames = self.hidden_frames(self.stack) | |||
|
701 | for i in range(self.curindex - 1, -1, -1): | |||
|
702 | frame = self.stack[i][0] | |||
|
703 | if hidden_frames[i] and self.skip_hidden: | |||
|
704 | skipped += 1 | |||
|
705 | continue | |||
|
706 | counter += 1 | |||
|
707 | if counter >= count: | |||
|
708 | break | |||
|
709 | else: | |||
|
710 | # if no break occured. | |||
|
711 | self.error("all frames above hidden") | |||
|
712 | return | |||
|
713 | ||||
|
714 | Colors = self.color_scheme_table.active_colors | |||
|
715 | ColorsNormal = Colors.Normal | |||
|
716 | _newframe = i | |||
|
717 | self._select_frame(_newframe) | |||
|
718 | if skipped: | |||
|
719 | print( | |||
|
720 | f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n" | |||
|
721 | ) | |||
|
722 | ||||
|
723 | def do_down(self, arg): | |||
|
724 | """d(own) [count] | |||
|
725 | Move the current frame count (default one) levels down in the | |||
|
726 | stack trace (to a newer frame). | |||
|
727 | ||||
|
728 | Will skip hidden frames. | |||
|
729 | """ | |||
|
730 | if self.curindex + 1 == len(self.stack): | |||
|
731 | self.error("Newest frame") | |||
|
732 | return | |||
|
733 | try: | |||
|
734 | count = int(arg or 1) | |||
|
735 | except ValueError: | |||
|
736 | self.error("Invalid frame count (%s)" % arg) | |||
|
737 | return | |||
|
738 | if count < 0: | |||
|
739 | _newframe = len(self.stack) - 1 | |||
|
740 | else: | |||
|
741 | _newindex = self.curindex | |||
|
742 | counter = 0 | |||
|
743 | skipped = 0 | |||
|
744 | hidden_frames = self.hidden_frames(self.stack) | |||
|
745 | for i in range(self.curindex + 1, len(self.stack)): | |||
|
746 | frame = self.stack[i][0] | |||
|
747 | if hidden_frames[i] and self.skip_hidden: | |||
|
748 | skipped += 1 | |||
|
749 | continue | |||
|
750 | counter += 1 | |||
|
751 | if counter >= count: | |||
|
752 | break | |||
|
753 | else: | |||
|
754 | self.error("all frames bellow hidden") | |||
|
755 | return | |||
|
756 | ||||
|
757 | Colors = self.color_scheme_table.active_colors | |||
|
758 | ColorsNormal = Colors.Normal | |||
|
759 | if skipped: | |||
|
760 | print( | |||
|
761 | f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n" | |||
|
762 | ) | |||
|
763 | _newframe = i | |||
|
764 | ||||
|
765 | self._select_frame(_newframe) | |||
|
766 | ||||
|
767 | do_d = do_down | |||
|
768 | do_u = do_up | |||
636 |
|
769 | |||
637 | class InterruptiblePdb(Pdb): |
|
770 | class InterruptiblePdb(Pdb): | |
638 | """Version of debugger where KeyboardInterrupt exits the debugger altogether.""" |
|
771 | """Version of debugger where KeyboardInterrupt exits the debugger altogether.""" |
@@ -2211,11 +2211,20 b' class InteractiveShell(SingletonConfigurable):' | |||||
2211 | with self.builtin_trap: |
|
2211 | with self.builtin_trap: | |
2212 | return self.Completer.complete(text, line, cursor_pos) |
|
2212 | return self.Completer.complete(text, line, cursor_pos) | |
2213 |
|
2213 | |||
2214 | def set_custom_completer(self, completer, pos=0): |
|
2214 | def set_custom_completer(self, completer, pos=0) -> None: | |
2215 | """Adds a new custom completer function. |
|
2215 | """Adds a new custom completer function. | |
2216 |
|
2216 | |||
2217 | The position argument (defaults to 0) is the index in the completers |
|
2217 | The position argument (defaults to 0) is the index in the completers | |
2218 |
list where you want the completer to be inserted. |
|
2218 | list where you want the completer to be inserted. | |
|
2219 | ||||
|
2220 | `completer` should have the following signature:: | |||
|
2221 | ||||
|
2222 | def completion(self: Completer, text: string) -> List[str]: | |||
|
2223 | raise NotImplementedError | |||
|
2224 | ||||
|
2225 | It will be bound to the current Completer instance and pass some text | |||
|
2226 | and return a list with current completions to suggest to the user. | |||
|
2227 | """ | |||
2219 |
|
2228 | |||
2220 | newcomp = types.MethodType(completer, self.Completer) |
|
2229 | newcomp = types.MethodType(completer, self.Completer) | |
2221 | self.Completer.custom_matchers.insert(pos,newcomp) |
|
2230 | self.Completer.custom_matchers.insert(pos,newcomp) | |
@@ -3310,6 +3319,9 b' class InteractiveShell(SingletonConfigurable):' | |||||
3310 | False : successful execution. |
|
3319 | False : successful execution. | |
3311 | True : an error occurred. |
|
3320 | True : an error occurred. | |
3312 | """ |
|
3321 | """ | |
|
3322 | # special value to say that anything above is IPython and should be | |||
|
3323 | # hidden. | |||
|
3324 | __tracebackhide__ = "__ipython_bottom__" | |||
3313 | # Set our own excepthook in case the user code tries to call it |
|
3325 | # Set our own excepthook in case the user code tries to call it | |
3314 | # directly, so that the IPython crash handler doesn't get triggered |
|
3326 | # directly, so that the IPython crash handler doesn't get triggered | |
3315 | old_excepthook, sys.excepthook = sys.excepthook, self.excepthook |
|
3327 | old_excepthook, sys.excepthook = sys.excepthook, self.excepthook |
@@ -364,13 +364,25 b' Currently the magic system has the following functions:""",' | |||||
364 |
|
364 | |||
365 | Valid modes: Plain, Context, Verbose, and Minimal. |
|
365 | Valid modes: Plain, Context, Verbose, and Minimal. | |
366 |
|
366 | |||
367 |
If called without arguments, acts as a toggle. |
|
367 | If called without arguments, acts as a toggle. | |
|
368 | ||||
|
369 | When in verbose mode the value --show (and --hide) | |||
|
370 | will respectively show (or hide) frames with ``__tracebackhide__ = | |||
|
371 | True`` value set. | |||
|
372 | """ | |||
368 |
|
373 | |||
369 | def xmode_switch_err(name): |
|
374 | def xmode_switch_err(name): | |
370 | warn('Error changing %s exception modes.\n%s' % |
|
375 | warn('Error changing %s exception modes.\n%s' % | |
371 | (name,sys.exc_info()[1])) |
|
376 | (name,sys.exc_info()[1])) | |
372 |
|
377 | |||
373 | shell = self.shell |
|
378 | shell = self.shell | |
|
379 | if parameter_s.strip() == "--show": | |||
|
380 | shell.InteractiveTB.skip_hidden = False | |||
|
381 | return | |||
|
382 | if parameter_s.strip() == "--hide": | |||
|
383 | shell.InteractiveTB.skip_hidden = True | |||
|
384 | return | |||
|
385 | ||||
374 | new_mode = parameter_s.strip().capitalize() |
|
386 | new_mode = parameter_s.strip().capitalize() | |
375 | try: |
|
387 | try: | |
376 | shell.InteractiveTB.set_mode(mode=new_mode) |
|
388 | shell.InteractiveTB.set_mode(mode=new_mode) |
@@ -4,20 +4,24 b'' | |||||
4 | # Copyright (c) IPython Development Team. |
|
4 | # Copyright (c) IPython Development Team. | |
5 | # Distributed under the terms of the Modified BSD License. |
|
5 | # Distributed under the terms of the Modified BSD License. | |
6 |
|
6 | |||
|
7 | import bdb | |||
|
8 | import builtins | |||
|
9 | import os | |||
7 | import signal |
|
10 | import signal | |
|
11 | import subprocess | |||
8 | import sys |
|
12 | import sys | |
9 | import time |
|
13 | import time | |
10 | import warnings |
|
14 | import warnings | |
|
15 | from subprocess import PIPE, CalledProcessError, check_output | |||
11 | from tempfile import NamedTemporaryFile |
|
16 | from tempfile import NamedTemporaryFile | |
12 | from subprocess import check_output, CalledProcessError, PIPE |
|
17 | from textwrap import dedent | |
13 | import subprocess |
|
|||
14 | from unittest.mock import patch |
|
18 | from unittest.mock import patch | |
15 | import builtins |
|
|||
16 | import bdb |
|
|||
17 |
|
19 | |||
18 | import nose.tools as nt |
|
20 | import nose.tools as nt | |
19 |
|
21 | |||
20 | from IPython.core import debugger |
|
22 | from IPython.core import debugger | |
|
23 | from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE | |||
|
24 | from IPython.testing.decorators import skip_win32 | |||
21 |
|
25 | |||
22 | #----------------------------------------------------------------------------- |
|
26 | #----------------------------------------------------------------------------- | |
23 | # Helper classes, from CPython's Pdb test suite |
|
27 | # Helper classes, from CPython's Pdb test suite | |
@@ -254,3 +258,69 b' def test_interruptible_core_debugger():' | |||||
254 | # implementation would involve a subprocess, but that adds issues with |
|
258 | # implementation would involve a subprocess, but that adds issues with | |
255 | # interrupting subprocesses that are rather complex, so it's simpler |
|
259 | # interrupting subprocesses that are rather complex, so it's simpler | |
256 | # just to do it this way. |
|
260 | # just to do it this way. | |
|
261 | ||||
|
262 | @skip_win32 | |||
|
263 | def test_xmode_skip(): | |||
|
264 | """that xmode skip frames | |||
|
265 | ||||
|
266 | Not as a doctest as pytest does not run doctests. | |||
|
267 | """ | |||
|
268 | import pexpect | |||
|
269 | env = os.environ.copy() | |||
|
270 | env["IPY_TEST_SIMPLE_PROMPT"] = "1" | |||
|
271 | ||||
|
272 | child = pexpect.spawn( | |||
|
273 | sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env | |||
|
274 | ) | |||
|
275 | child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE | |||
|
276 | ||||
|
277 | child.expect("IPython") | |||
|
278 | child.expect("\n") | |||
|
279 | child.expect_exact("In [1]") | |||
|
280 | ||||
|
281 | block = dedent( | |||
|
282 | """ | |||
|
283 | def f(): | |||
|
284 | __tracebackhide__ = True | |||
|
285 | g() | |||
|
286 | ||||
|
287 | def g(): | |||
|
288 | raise ValueError | |||
|
289 | ||||
|
290 | f() | |||
|
291 | """ | |||
|
292 | ) | |||
|
293 | ||||
|
294 | for line in block.splitlines(): | |||
|
295 | child.sendline(line) | |||
|
296 | child.expect_exact(line) | |||
|
297 | child.expect_exact("skipping") | |||
|
298 | ||||
|
299 | block = dedent( | |||
|
300 | """ | |||
|
301 | def f(): | |||
|
302 | __tracebackhide__ = True | |||
|
303 | g() | |||
|
304 | ||||
|
305 | def g(): | |||
|
306 | from IPython.core.debugger import set_trace | |||
|
307 | set_trace() | |||
|
308 | ||||
|
309 | f() | |||
|
310 | """ | |||
|
311 | ) | |||
|
312 | ||||
|
313 | for line in block.splitlines(): | |||
|
314 | child.sendline(line) | |||
|
315 | child.expect_exact(line) | |||
|
316 | ||||
|
317 | child.expect("ipdb>") | |||
|
318 | child.sendline("w") | |||
|
319 | child.expect("hidden") | |||
|
320 | child.expect("ipdb>") | |||
|
321 | child.sendline("skip_hidden false") | |||
|
322 | child.sendline("w") | |||
|
323 | child.expect("__traceba") | |||
|
324 | child.expect("ipdb>") | |||
|
325 | ||||
|
326 | child.close() |
@@ -879,13 +879,36 b' class VerboseTB(TBTools):' | |||||
879 | self.check_cache = check_cache |
|
879 | self.check_cache = check_cache | |
880 |
|
880 | |||
881 | self.debugger_cls = debugger_cls or debugger.Pdb |
|
881 | self.debugger_cls = debugger_cls or debugger.Pdb | |
|
882 | self.skip_hidden = True | |||
882 |
|
883 | |||
883 | def format_records(self, records, last_unique, recursion_repeat): |
|
884 | def format_records(self, records, last_unique, recursion_repeat): | |
884 | """Format the stack frames of the traceback""" |
|
885 | """Format the stack frames of the traceback""" | |
885 | frames = [] |
|
886 | frames = [] | |
|
887 | ||||
|
888 | skipped = 0 | |||
886 | for r in records[:last_unique+recursion_repeat+1]: |
|
889 | for r in records[:last_unique+recursion_repeat+1]: | |
887 | #print '*** record:',file,lnum,func,lines,index # dbg |
|
890 | if self.skip_hidden: | |
|
891 | if r[0].f_locals.get("__tracebackhide__", 0): | |||
|
892 | skipped += 1 | |||
|
893 | continue | |||
|
894 | if skipped: | |||
|
895 | Colors = self.Colors # just a shorthand + quicker name lookup | |||
|
896 | ColorsNormal = Colors.Normal # used a lot | |||
|
897 | frames.append( | |||
|
898 | " %s[... skipping hidden %s frame]%s\n" | |||
|
899 | % (Colors.excName, skipped, ColorsNormal) | |||
|
900 | ) | |||
|
901 | skipped = 0 | |||
|
902 | ||||
888 | frames.append(self.format_record(*r)) |
|
903 | frames.append(self.format_record(*r)) | |
|
904 | ||||
|
905 | if skipped: | |||
|
906 | Colors = self.Colors # just a shorthand + quicker name lookup | |||
|
907 | ColorsNormal = Colors.Normal # used a lot | |||
|
908 | frames.append( | |||
|
909 | " %s[... skipping hidden %s frame]%s\n" | |||
|
910 | % (Colors.excName, skipped, ColorsNormal) | |||
|
911 | ) | |||
889 |
|
912 | |||
890 | if recursion_repeat: |
|
913 | if recursion_repeat: | |
891 | frames.append('... last %d frames repeated, from the frame below ...\n' % recursion_repeat) |
|
914 | frames.append('... last %d frames repeated, from the frame below ...\n' % recursion_repeat) | |
@@ -1123,8 +1146,6 b' class VerboseTB(TBTools):' | |||||
1123 | head = self.prepare_header(etype, self.long_header) |
|
1146 | head = self.prepare_header(etype, self.long_header) | |
1124 | records = self.get_records(etb, number_of_lines_of_context, tb_offset) |
|
1147 | records = self.get_records(etb, number_of_lines_of_context, tb_offset) | |
1125 |
|
1148 | |||
1126 | if records is None: |
|
|||
1127 | return "" |
|
|||
1128 |
|
1149 | |||
1129 | last_unique, recursion_repeat = find_recursion(orig_etype, evalue, records) |
|
1150 | last_unique, recursion_repeat = find_recursion(orig_etype, evalue, records) | |
1130 |
|
1151 |
@@ -41,6 +41,16 b' class TerminalPdb(Pdb):' | |||||
41 | global_namespace={}, |
|
41 | global_namespace={}, | |
42 | parent=self.shell, |
|
42 | parent=self.shell, | |
43 | ) |
|
43 | ) | |
|
44 | # add a completer for all the do_ methods | |||
|
45 | methods_names = [m[3:] for m in dir(self) if m.startswith("do_")] | |||
|
46 | ||||
|
47 | def gen_comp(self, text): | |||
|
48 | return [m for m in methods_names if m.startswith(text)] | |||
|
49 | import types | |||
|
50 | newcomp = types.MethodType(gen_comp, compl) | |||
|
51 | compl.custom_matchers.insert(0, newcomp) | |||
|
52 | # end add completer. | |||
|
53 | ||||
44 | self._ptcomp = IPythonPTCompleter(compl) |
|
54 | self._ptcomp = IPythonPTCompleter(compl) | |
45 |
|
55 | |||
46 | options = dict( |
|
56 | options = dict( |
@@ -4,13 +4,6 b' matrix:' | |||||
4 |
|
4 | |||
5 | environment: |
|
5 | environment: | |
6 | matrix: |
|
6 | matrix: | |
7 | - PYTHON: "C:\\Python36" |
|
|||
8 | PYTHON_VERSION: "3.6.x" |
|
|||
9 | PYTHON_ARCH: "32" |
|
|||
10 |
|
||||
11 | - PYTHON: "C:\\Python36-x64" |
|
|||
12 | PYTHON_VERSION: "3.6.x" |
|
|||
13 | PYTHON_ARCH: "64" |
|
|||
14 |
|
7 | |||
15 | - PYTHON: "C:\\Python37-x64" |
|
8 | - PYTHON: "C:\\Python37-x64" | |
16 | PYTHON_VERSION: "3.7.x" |
|
9 | PYTHON_VERSION: "3.7.x" |
General Comments 0
You need to be logged in to leave comments.
Login now