Coverage for src/turtlesc/__init__.py: 100%
338 statements
« prev ^ index » next coverage.py v7.7.0, created at 2025-03-18 22:46 -0400
« prev ^ index » next coverage.py v7.7.0, created at 2025-03-18 22:46 -0400
1import turtle, time, re
3ALL_SHORTCUTS = 'f b l r h c g tele x y st u pd pu ps pc fc bc sh cir undo bf ef sleep n s e w nw ne sw se u t cs css spd' + \
4 'forward backward left right home clear goto setx sety stamp update pendown penup pensize pencolor fillcolor bgcolor setheading' + \
5 'circle undo begin_fill end_fill north south east west northwest northeast southwest southeast reset bye done exitonclick update' + \
6 'tracer hide show dot clearstamp clearstamps degrees radians speed'
8CARDINAL_TO_DEGREES = {'n': '90', 's': '270', 'e': '0', 'w': '180', 'nw': '135', 'ne': '45', 'sw': '225', 'se': '315'}
10_MAP_FULL_TO_SHORT_NAMES = {'forward': 'f', 'backward': 'b', 'right': 'r', 'left': 'l', 'home': 'h', 'clear': 'c',
11 'goto': 'g', 'teleport': 'tele', 'setx': 'x', 'sety': 'y', 'stamp': 'st', 'update': 'u', 'pendown': 'pd', 'penup': 'pu',
12 'pensize': 'ps', 'pencolor': 'pc', 'fillcolor': 'fc', 'bgcolor': 'bc', 'setheading': 'sh', 'circle': 'cir',
13 'begin_fill': 'bf', 'end_fill': 'ef', 'north': 'n', 'south': 's', 'east': 'e', 'west': 'w',
14 'northwest': 'nw', 'northeast': 'ne', 'southwest': 'sw', 'southeast': 'se', 'update': 'u', 'tracer': 't',
15 'clearstamp': 'cs', 'clearstamps': 'css', 'speed': 'spd'}
17class TurtleShortcutException(Exception):
18 pass
20def sc(*args, turtle_obj=None, _return_turtle_code=False): # type: () -> int
21 """TODO
22 """
24 """Supported commands:
26 f N - forward(N)
27 b N - backward(N)
28 l N - left(N)
29 r N - right(N)
30 h - home()
31 c - clear()
32 g X Y - goto(X, Y)
33 tele X Y - teleport(X, Y)
34 x X - setx(X)
35 y Y - sety(Y)
36 st - stamp()
37 pd - pendown()
38 pu - penup()
39 ps N - pensize(N)
40 pc RGB - pencolor(RGB) (RGB value can either be a single string like `red` or three dec/hex numbers `1.0 0.0 0.5` or `FF FF 00`
41 fc RGB - fillcolor(RGB)
42 bc RGB - bgcolor(RGB)
43 sh N - setheading(N)
44 cir N - circle(N)
45 undo - undo()
46 bf - begin_fill()
47 ef - end_fill()
48 reset - reset()
50 sleep N - time.sleep(N)
52 n N - setheading(90);forward(N)
53 s N - setheading(270);forward(N)
54 w N - setheading(180);forward(N)
55 e N - setheading(0);forward(N)
56 nw N - setheading(135);forward(N)
57 ne N - setheading(45);forward(N)
58 sw N - setheading(225);forward(N)
59 se N - setheading(315);forward(N)
60 north N - setheading(90);forward(N)
61 south N - setheading(270);forward(N)
62 west N - setheading(180);forward(N)
63 east N - setheading(0);forward(N)
64 northwest N - setheading(135);forward(N)
65 northeast N - setheading(45);forward(N)
66 southwest N - setheading(225);forward(N)
67 southeast N - setheading(315);forward(N)
69 done - done()
70 bye - bye()
71 exitonclick - exitonclick()
73 t N1 N2 - tracer(N1, N2)
74 u - update()
76 hide - hide()
77 show - show()
79 dot N - dot(N)
80 cs N - clearstamp(N)
81 css N - clearstamps(N)
82 degrees - degrees()
83 radians - radians()
85 spd N - speed(N) but N can also be 'fastest', 'fast', 'normal', 'slow', 'slowest'
86 !!shape N - shape(N) where N can be “arrow”, “turtle”, “circle”, “square”, “triangle”, “classic”
87 !!resizemode N - resizemode(N) where N can be “auto”, “user”, or "noresize"
88 !!bgpic N - bgpic(N) where the N filename cannot have a comma in it.
90 !!shapesize N1 N2 N3 - shapesize(N1, N2, N3)
91 !!settiltangle N - settiltangle(N)
92 !!tilt N - tilt(N)
93 !!tiltangle N - tiltangle(N)
98 Note:
101 Furthermore, you can also use the full names: forward N translates to forward(N).
102 Note: None of these functions can take string args that have spaces in them, since spaces are the arg delimiter here.
103 Note: You also can't use variables here, only static values. But you can use f-strings.
105 Return value is the number of commands executed.
106 Whitespace is insignificant. ' f 100 ' is the same as 'f 100'
107 """
109 """
112 """
113 # Join multiple arg strings into one, separated by commas:
114 shortcuts = ','.join(args)
116 # Newlines become commas as well:
117 shortcuts = shortcuts.replace('\n', ',')
119 if shortcuts == '' or len(shortcuts.split(',')) == 0:
120 return 0
122 count_of_shortcuts_run = 0
124 # Go through and check that all shortcuts are syntactically correct:
125 for shortcut in shortcuts.split(','):
126 count_of_shortcuts_run += _run_shortcut(shortcut, turtle_obj=turtle_obj, dry_run=True)
128 # Go through and actually run all the shortcuts:
129 count_of_shortcuts_run = 0
130 turtle_code = tuple()
131 for shortcut in shortcuts.split(','):
132 if _return_turtle_code:
133 turtle_code += _run_shortcut(shortcut, turtle_obj=turtle_obj, _return_turtle_code=True)
134 else:
135 count_of_shortcuts_run += _run_shortcut(shortcut, turtle_obj=turtle_obj)
137 if _return_turtle_code:
138 return turtle_code
139 else:
140 return count_of_shortcuts_run
143def _run_shortcut(shortcut, turtle_obj=None, dry_run=False, _return_turtle_code=False):
144 if turtle_obj is None:
145 turtle_obj = turtle # Use the main turtle given by the module.
147 # Clean up shortcut name from " FOrWARD " to "f", for example.
148 shortcut_parts = shortcut.strip().split()
149 if len(shortcut_parts) == 0:
150 return 0
151 _sc = shortcut_parts[0].lower()
152 _sc = _MAP_FULL_TO_SHORT_NAMES.get(_sc, _sc)
154 # Check that the shortcut's syntax is valid:
156 if _sc not in ALL_SHORTCUTS:
157 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: `' + shortcut_parts[0] + '` is not a turtle shortcut.')
159 raise_exception = False
160 count_of_shortcuts_run = 0
164 # SHORTCUTS THAT TAKE A SINGLE NUMERIC ARGUMENT:
165 if _sc in ('f', 'b', 'r', 'l', 'x', 'y', 'ps', 'sh', 'cir', 'sleep', 'n', 's', 'e', 'w', 'nw', 'ne', 'sw', 'se', 'dot', 'cs', 'spd'):
166 if len(shortcut_parts) < 2:
167 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: Missing the required numeric argument.')
168 if len(shortcut_parts) > 2:
169 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: Too many arguments.')
171 # Convert the string arguments for the `speed` shortcut to their numeric equivalents.
172 if _sc == 'spd':
173 shortcut_parts[1] = {'fastest': 0, 'fast': 10, 'normal': 6, 'slow': 3, 'slowest': 1}.get(shortcut_parts[1].lower(), shortcut_parts[1].lower())
175 try:
176 float(shortcut_parts[1])
177 except ValueError:
178 raise_exception = True # We don't raise here so we can hide the original ValueError and make the stack trace a bit neater.
179 if raise_exception:
180 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: `' + shortcut_parts[1] + '` is not a number.')
182 # `dot` shortcut doesn't allow negative values:
183 if _sc == 'dot' and float(shortcut_parts[1]) < 0:
184 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: `dot` argument cannot be a negative number.')
186 if not dry_run:
187 # Run the shortcut that has exactly one numeric argument:
188 if _sc == 'f':
189 if _return_turtle_code:
190 return ('forward(' + shortcut_parts[1] + ')',)
191 turtle_obj.forward(float(shortcut_parts[1]))
192 elif _sc == 'b':
193 if _return_turtle_code:
194 return ('backward(' + shortcut_parts[1] + ')',)
195 turtle_obj.backward(float(shortcut_parts[1]))
196 elif _sc == 'r':
197 if _return_turtle_code:
198 return ('right(' + shortcut_parts[1] + ')',)
199 turtle_obj.right(float(shortcut_parts[1]))
200 elif _sc == 'l':
201 if _return_turtle_code:
202 return ('left(' + shortcut_parts[1] + ')',)
203 turtle_obj.left(float(shortcut_parts[1]))
204 elif _sc == 'x':
205 if _return_turtle_code:
206 return ('setx(' + shortcut_parts[1] + ')',)
207 turtle_obj.setx(float(shortcut_parts[1]))
208 elif _sc == 'y':
209 if _return_turtle_code:
210 return ('sety(' + shortcut_parts[1] + ')',)
211 turtle_obj.sety(float(shortcut_parts[1]))
212 elif _sc == 'ps':
213 if _return_turtle_code:
214 return ('pensize(' + shortcut_parts[1] + ')',)
215 turtle_obj.pensize(float(shortcut_parts[1]))
216 elif _sc == 'sh':
217 if _return_turtle_code:
218 return ('setheading(' + shortcut_parts[1] + ')',)
219 turtle_obj.setheading(float(shortcut_parts[1]))
220 elif _sc == 'cir':
221 if _return_turtle_code:
222 return ('circle(' + shortcut_parts[1] + ')',)
223 turtle_obj.circle(float(shortcut_parts[1]))
224 elif _sc == 'sleep':
225 if _return_turtle_code:
226 return ('sleep(' + shortcut_parts[1] + ')', )
227 time.sleep(float(shortcut_parts[1]))
228 elif _sc in ('n', 's', 'e', 'w', 'nw', 'ne', 'sw', 'se'):
229 originally_in_radians_mode = in_radians_mode()
231 if _return_turtle_code:
232 if originally_in_radians_mode:
233 return ('degrees()', 'setheading(' + CARDINAL_TO_DEGREES[_sc] + ')', 'forward(' + shortcut_parts[1] + ')', 'radians()')
234 else:
235 return ('setheading(' + CARDINAL_TO_DEGREES[_sc] + ')', 'forward(' + shortcut_parts[1] + ')')
236 turtle.degrees()
237 if _sc == 'n':
238 turtle.setheading(90)
239 elif _sc == 's':
240 turtle.setheading(270)
241 elif _sc == 'e':
242 turtle.setheading(0)
243 elif _sc == 'w':
244 turtle.setheading(180)
245 elif _sc == 'nw':
246 turtle.setheading(135)
247 elif _sc == 'ne':
248 turtle.setheading(45)
249 elif _sc == 'sw':
250 turtle.setheading(225)
251 elif _sc == 'se':
252 turtle.setheading(315)
253 else: # pragma: no cover
254 assert False, 'Unhandled shortcut: ' + _sc
255 turtle_obj.forward(float(shortcut_parts[1]))
256 if originally_in_radians_mode:
257 turtle.radians()
258 elif _sc == 'dot':
259 if _return_turtle_code:
260 return ('dot(' + shortcut_parts[1] + ')',)
261 turtle_obj.dot(float(shortcut_parts[1]))
262 elif _sc == 'cs':
263 if _return_turtle_code:
264 return ('clearstamp(' + shortcut_parts[1] + ')',)
265 turtle_obj.clearstamp(float(shortcut_parts[1]))
266 elif _sc == 'spd':
267 if _return_turtle_code:
268 return ('speed(' + str(shortcut_parts[1]) + ')',)
269 turtle_obj.speed(float(shortcut_parts[1]))
270 else: # pragma: no cover
271 assert False, 'Unhandled shortcut: ' + _sc
272 count_of_shortcuts_run += 1
278 # SHORTCUTS THAT TAKE A SINGLE INTEGER ARGUMENT OR NONE ARGUMENT:
279 elif _sc in ('css',):
280 if len(shortcut_parts) > 2:
281 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: Too many arguments.')
283 # Technically, the css shortcut can take a float argument, but it gets passed to int() silently. Not ideal, but not a big deal either.
285 if len(shortcut_parts) == 2:
286 try:
287 int(shortcut_parts[1])
288 except ValueError:
289 raise_exception = True # We don't raise here so we can hide the original ValueError and make the stack trace a bit neater.
290 if raise_exception:
291 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: `' + shortcut_parts[1] + '` is not a number.')
293 if not dry_run:
294 # Run the shortcut:
295 if _sc == 'css':
296 if len(shortcut_parts) == 1:
297 if _return_turtle_code:
298 return ('clearstamps()',)
299 turtle_obj.clearstamps()
300 elif len(shortcut_parts) == 2:
301 if _return_turtle_code:
302 return ('clearstamps(' + shortcut_parts[1] + ')',)
303 turtle_obj.clearstamps(int(shortcut_parts[1]))
304 else: # pragma: no cover
305 assert False, 'Unhandled shortcut: ' + _sc
306 else: # pragma: no cover
307 assert False, 'Unhandled shortcut: ' + _sc
308 count_of_shortcuts_run += 1
315 # SHORTCUTS THAT TAKE EXACTLY TWO NUMERIC ARGUMENTS:
316 elif _sc in ('g', 't', 'tele'):
317 if len(shortcut_parts) < 3:
318 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: Missing two required numeric argument.')
319 elif len(shortcut_parts) > 3:
320 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: Too many arguments.')
322 try:
323 float(shortcut_parts[1])
324 except ValueError:
325 raise_exception = True # We don't raise here so we can hide the original ValueError and make the stack trace a bit neater.
326 if raise_exception:
327 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: `' + shortcut_parts[1] + '` is not a number.')
328 try:
329 float(shortcut_parts[2])
330 except ValueError:
331 raise_exception = True # We don't raise here so we can hide the original ValueError and make the stack trace a bit neater.
332 if raise_exception:
333 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: `' + shortcut_parts[2] + '` is not a number.')
335 if not dry_run:
336 # Run the shortcut that has exactly two numeric arguments:
337 x = float(shortcut_parts[1])
338 y = float(shortcut_parts[2])
340 # Run the shortcut:
341 if _sc == 'g':
342 if _return_turtle_code:
343 return ('goto(' + shortcut_parts[1] + ', ' + shortcut_parts[2] + ')',)
344 turtle_obj.goto(x, y)
345 elif _sc == 't':
346 if _return_turtle_code:
347 return ('tracer(' + shortcut_parts[1] + ', ' + shortcut_parts[2] + ')',)
348 turtle.tracer(x, y) # Note: tracer() is not a Turtle method, there's only the global tracer() function.
349 elif _sc == 'tele':
350 if _return_turtle_code:
351 return ('teleport(' + shortcut_parts[1] + ', ' + shortcut_parts[2] + ')',)
352 turtle_obj.teleport(x, y)
353 else: # pragma: no cover
354 assert False, 'Unhandled shortcut: ' + _sc
355 count_of_shortcuts_run += 1
361 # SHORTCUTS THAT TAKE EXACTLY ZERO ARGUMENTS:
362 elif _sc in ('h', 'c', 'st', 'pd', 'pu', 'undo', 'bf', 'ef', 'reset', 'bye', 'done', 'exitonclick', 'u', 'show', 'hide'):
363 if len(shortcut_parts) > 1:
364 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: This shortcut does not have arguments.')
366 if not dry_run:
367 # Run the shortcut that has exactly zero arguments:
368 if _sc == 'h':
369 if _return_turtle_code:
370 return ('home()',)
371 turtle_obj.home()
372 elif _sc == 'c':
373 if _return_turtle_code:
374 return ('clear()',)
375 turtle_obj.clear()
376 elif _sc == 'st':
377 if _return_turtle_code:
378 return ('stamp()',)
379 turtle_obj.stamp()
380 elif _sc == 'pd':
381 if _return_turtle_code:
382 return ('pendown()',)
383 turtle_obj.pendown()
384 elif _sc == 'pu':
385 if _return_turtle_code:
386 return ('penup()',)
387 turtle_obj.penup()
388 elif _sc == 'undo':
389 if _return_turtle_code:
390 return ('undo()',)
391 turtle_obj.undo()
392 elif _sc == 'bf':
393 if _return_turtle_code:
394 return ('begin_fill()',)
395 turtle_obj.begin_fill()
396 elif _sc == 'ef':
397 if _return_turtle_code:
398 return ('end_fill()',)
399 turtle_obj.end_fill()
400 elif _sc == 'reset':
401 if _return_turtle_code:
402 return ('reset()',)
403 turtle_obj.reset()
404 elif _sc == 'bye': # pragma: no cover
405 if _return_turtle_code:
406 return ('bye()',)
407 turtle_obj.bye()
408 elif _sc == 'done': # pragma: no cover
409 if _return_turtle_code:
410 return ('done()',)
411 turtle_obj.done()
412 elif _sc == 'exitonclick': # pragma: no cover
413 if _return_turtle_code:
414 return ('exitonclick()',)
415 turtle_obj.exitonclick()
416 elif _sc == 'u':
417 if _return_turtle_code:
418 return ('update()',)
419 turtle_obj.update()
420 elif _sc == 'show':
421 if _return_turtle_code:
422 return ('showturtle()',)
423 turtle_obj.showturtle()
424 elif _sc == 'hide':
425 if _return_turtle_code:
426 return ('hideturtle()',)
427 turtle_obj.hideturtle()
428 else: # pragma: no cover
429 assert False, 'Unhandled shortcut: ' + _sc
430 count_of_shortcuts_run += 1
434 # SHORTCUTS THAT TAKE AN RGB OR COLOR ARGUMENT:
435 elif _sc in ('pc', 'fc', 'bc'):
436 if len(shortcut_parts) < 2:
437 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: Missing required RGB argument.')
438 elif len(shortcut_parts) not in (2, 4):
439 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: Invalid RGB argument. It must either be a color name like `red` or three numbers like `1.0 0.5 0.0` or `255 0 255` or `FF 00 FF`.')
441 color_arg_is_color_name = False
443 if len(shortcut_parts) == 4:
444 # We expect the color arg to either be something like (255, 0, 0) or (1.0, 0.0, 0.0):
445 raise_exception = False
447 try:
448 float(shortcut_parts[1])
449 except ValueError:
450 raise_exception = True # We don't raise here so we can hide the original ValueError and make the stack trace a bit neater.
451 if raise_exception:
452 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: `' + shortcut_parts[1] + '` is not a number.')
454 try:
455 float(shortcut_parts[2])
456 except ValueError:
457 raise_exception = True # We don't raise here so we can hide the original ValueError and make the stack trace a bit neater.
458 if raise_exception:
459 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: `' + shortcut_parts[2] + '` is not a number.')
461 try:
462 float(shortcut_parts[3])
463 except ValueError:
464 raise_exception = True # We don't raise here so we can hide the original ValueError and make the stack trace a bit neater.
465 if raise_exception:
466 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: `' + shortcut_parts[3] + '` is not a number.')
468 color_arg = (float(shortcut_parts[1]), float(shortcut_parts[2]), float(shortcut_parts[3]))
469 elif len(shortcut_parts) == 2:
470 # We expect the color arg to be a string like 'blue' or 'ff0000' or '#FF0000:
471 raise_exception = False
473 # If the argument is an RGB value, convert to numbers:
474 shortcut_parts[1] = shortcut_parts[1].strip('#')
476 # Lowercase possible color names:
477 shortcut_parts[1] = shortcut_parts[1].lower()
479 if re.match(r'^[0-9a-f]{6}$', shortcut_parts[1]):
480 # Convert hex color to decimal color:
481 if turtle_obj.colormode() == 255:
482 color_arg = (int(shortcut_parts[1][0:2], 16), int(shortcut_parts[1][2:4], 16), int(shortcut_parts[1][4:6], 16))
483 elif turtle_obj.colormode() == 1.0:
484 color_arg = (int(shortcut_parts[1][0:2], 16) / 255.0, int(shortcut_parts[1][2:4], 16) / 255.0, int(shortcut_parts[1][4:6], 16) / 255.0)
485 else: # pragma: no cover
486 assert False, 'Unknown return value from turtle.colormode(): ' + str(turtle_obj.colormode())
487 else:
488 # shortcut_parts[1] must be a color name like 'blue'
489 color_arg = shortcut_parts[1]
490 color_arg_is_color_name = True
492 # Test the color name by actually calling pencolor():
493 original_pen_color = turtle_obj.pencolor()
494 try:
495 turtle_obj.pencolor(color_arg)
496 except turtle.TurtleGraphicsError:
497 raise_exception = True # We don't raise here so we can hide the original TurtleGraphicsError and make the stack trace a bit neater.
498 if raise_exception:
499 raise TurtleShortcutException('Syntax error in `' + shortcut + '`: `' + shortcut_parts[1] + '` is not a valid color.')
500 turtle_obj.pencolor(original_pen_color)
502 if not dry_run:
503 if isinstance(color_arg, tuple):
504 temp_switch_to_mode_255 = len(color_arg) == 3 and turtle_obj.colormode() == 1.0 and (color_arg[0] > 1.0 or color_arg[1] > 1.0 or color_arg[2] > 1.0)
505 temp_switch_to_mode_1 = len(color_arg) == 3 and turtle_obj.colormode() == 255 and (0.0 <= color_arg[0] <= 1.0 and 0.0 <= color_arg[1] <= 1.0 and 0.0 <= color_arg[2] <= 1.0)
506 assert not (temp_switch_to_mode_255 and temp_switch_to_mode_1)
507 else:
508 temp_switch_to_mode_255 = False
509 temp_switch_to_mode_1 = False
511 if temp_switch_to_mode_255:
512 turtle_obj.colormode(255)
513 color_arg = (int(color_arg[0]), int(color_arg[1]), int(color_arg[2]))
514 elif temp_switch_to_mode_1:
515 turtle_obj.colormode(1.0)
517 # Return the turtle code, if that was asked:
518 if _return_turtle_code:
519 if _sc == 'pc':
520 func_name_prefix = 'pen'
521 elif _sc == 'fc':
522 func_name_prefix = 'fill'
523 elif _sc == 'bc':
524 func_name_prefix = 'bg'
526 if temp_switch_to_mode_255:
527 turtle_obj.colormode(1.0)
528 return ('colormode(255)', func_name_prefix + 'color(' + str(color_arg) + ')', 'colormode(1.0)')
529 elif temp_switch_to_mode_1:
530 turtle_obj.colormode(255)
531 return ('colormode(1.0)', func_name_prefix + 'color(' + str(color_arg) + ')', 'colormode(255)')
532 else:
533 if color_arg_is_color_name:
534 return (func_name_prefix + "color('" + str(color_arg) + "')",)
535 else:
536 return (func_name_prefix + 'color(' + str(color_arg) + ')',)
539 # Run the shortcut that has an RGB color argument:
540 if _sc == 'pc':
541 turtle_obj.pencolor(color_arg)
542 elif _sc == 'fc':
543 turtle_obj.fillcolor(color_arg)
544 elif _sc == 'bc':
545 turtle_obj.bgcolor(color_arg)
546 else: # pragma: no cover
547 assert False, 'Unhandled shortcut: ' + _sc
548 count_of_shortcuts_run += 1
550 if temp_switch_to_mode_255:
551 turtle_obj.colormode(1.0)
552 elif temp_switch_to_mode_1:
553 turtle_obj.colormode(255)
555 return count_of_shortcuts_run
558def in_radians_mode():
559 """Returns True if turtle is in radians mode, False if in degrees mode."""
560 original_heading = turtle.heading()
561 turtle.left(1)
562 turtle.radians() # Switch to radians mode.
563 turtle.right(1)
564 if turtle.heading() == original_heading:
565 return True
566 else:
567 turtle.degrees() # Switch back to degrees mode.
568 return False
571def in_degrees_mode():
572 """Returns True if turtle is in degrees mode, False if in radians mode."""
573 return not in_radians_mode()
576def get_turtle_code(*args):
577 """Returns the Python code that would be executed by the sc() function."""
578 return sc(*args, _return_turtle_code=True)