mirror of https://github.com/python/cpython
gh-123614: Add save function to turtle.py (#123617)
This commit is contained in:
parent
1f9d163850
commit
584cdf8d41
|
@ -427,6 +427,7 @@ Input methods
|
|||
Methods specific to Screen
|
||||
| :func:`bye`
|
||||
| :func:`exitonclick`
|
||||
| :func:`save`
|
||||
| :func:`setup`
|
||||
| :func:`title`
|
||||
|
||||
|
@ -2269,6 +2270,24 @@ Methods specific to Screen, not inherited from TurtleScreen
|
|||
client script.
|
||||
|
||||
|
||||
.. function:: save(filename, overwrite=False)
|
||||
|
||||
Save the current turtle drawing (and turtles) as a PostScript file.
|
||||
|
||||
:param filename: the path of the saved PostScript file
|
||||
:param overwrite: if ``False`` and there already exists a file with the given
|
||||
filename, then the function will raise a
|
||||
``FileExistsError``. If it is ``True``, the file will be
|
||||
overwritten.
|
||||
|
||||
.. doctest::
|
||||
:skipif: _tkinter is None
|
||||
|
||||
>>> screen.save("my_drawing.ps")
|
||||
>>> screen.save("my_drawing.ps", overwrite=True)
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. function:: setup(width=_CFG["width"], height=_CFG["height"], startx=_CFG["leftright"], starty=_CFG["topbottom"])
|
||||
|
||||
Set the size and position of the main window. Default values of arguments
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import os
|
||||
import pickle
|
||||
import re
|
||||
import unittest
|
||||
import unittest.mock
|
||||
import tempfile
|
||||
from test import support
|
||||
from test.support import import_helper
|
||||
from test.support import os_helper
|
||||
|
@ -130,6 +134,7 @@ class VectorComparisonMixin:
|
|||
self.assertAlmostEqual(
|
||||
i, j, msg='values at index {} do not match'.format(idx))
|
||||
|
||||
|
||||
class Multiplier:
|
||||
|
||||
def __mul__(self, other):
|
||||
|
@ -461,6 +466,67 @@ class TestTPen(unittest.TestCase):
|
|||
self.assertTrue(tpen.isdown())
|
||||
|
||||
|
||||
class TestTurtleScreen(unittest.TestCase):
|
||||
def test_save_raises_if_wrong_extension(self) -> None:
|
||||
screen = unittest.mock.Mock()
|
||||
|
||||
msg = "Unknown file extension: '.png', must be one of {'.ps', '.eps'}"
|
||||
with (
|
||||
tempfile.TemporaryDirectory() as tmpdir,
|
||||
self.assertRaisesRegex(ValueError, re.escape(msg))
|
||||
):
|
||||
turtle.TurtleScreen.save(screen, os.path.join(tmpdir, "file.png"))
|
||||
|
||||
def test_save_raises_if_parent_not_found(self) -> None:
|
||||
screen = unittest.mock.Mock()
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
parent = os.path.join(tmpdir, "unknown_parent")
|
||||
msg = f"The directory '{parent}' does not exist. Cannot save to it"
|
||||
|
||||
with self.assertRaisesRegex(FileNotFoundError, re.escape(msg)):
|
||||
turtle.TurtleScreen.save(screen, os.path.join(parent, "a.ps"))
|
||||
|
||||
def test_save_raises_if_file_found(self) -> None:
|
||||
screen = unittest.mock.Mock()
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
file_path = os.path.join(tmpdir, "some_file.ps")
|
||||
with open(file_path, "w") as f:
|
||||
f.write("some text")
|
||||
|
||||
msg = (
|
||||
f"The file '{file_path}' already exists. To overwrite it use"
|
||||
" the 'overwrite=True' argument of the save function."
|
||||
)
|
||||
with self.assertRaisesRegex(FileExistsError, re.escape(msg)):
|
||||
turtle.TurtleScreen.save(screen, file_path)
|
||||
|
||||
def test_save_overwrites_if_specified(self) -> None:
|
||||
screen = unittest.mock.Mock()
|
||||
screen.cv.postscript.return_value = "postscript"
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
file_path = os.path.join(tmpdir, "some_file.ps")
|
||||
with open(file_path, "w") as f:
|
||||
f.write("some text")
|
||||
|
||||
turtle.TurtleScreen.save(screen, file_path, overwrite=True)
|
||||
with open(file_path) as f:
|
||||
assert f.read() == "postscript"
|
||||
|
||||
def test_save(self) -> None:
|
||||
screen = unittest.mock.Mock()
|
||||
screen.cv.postscript.return_value = "postscript"
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
file_path = os.path.join(tmpdir, "some_file.ps")
|
||||
|
||||
turtle.TurtleScreen.save(screen, file_path)
|
||||
with open(file_path) as f:
|
||||
assert f.read() == "postscript"
|
||||
|
||||
|
||||
class TestModuleLevel(unittest.TestCase):
|
||||
def test_all_signatures(self):
|
||||
import inspect
|
||||
|
|
|
@ -106,6 +106,7 @@ import inspect
|
|||
import sys
|
||||
|
||||
from os.path import isfile, split, join
|
||||
from pathlib import Path
|
||||
from copy import deepcopy
|
||||
from tkinter import simpledialog
|
||||
|
||||
|
@ -115,7 +116,7 @@ _tg_screen_functions = ['addshape', 'bgcolor', 'bgpic', 'bye',
|
|||
'clearscreen', 'colormode', 'delay', 'exitonclick', 'getcanvas',
|
||||
'getshapes', 'listen', 'mainloop', 'mode', 'numinput',
|
||||
'onkey', 'onkeypress', 'onkeyrelease', 'onscreenclick', 'ontimer',
|
||||
'register_shape', 'resetscreen', 'screensize', 'setup',
|
||||
'register_shape', 'resetscreen', 'screensize', 'save', 'setup',
|
||||
'setworldcoordinates', 'textinput', 'title', 'tracer', 'turtles', 'update',
|
||||
'window_height', 'window_width']
|
||||
_tg_turtle_functions = ['back', 'backward', 'begin_fill', 'begin_poly', 'bk',
|
||||
|
@ -1492,6 +1493,39 @@ class TurtleScreen(TurtleScreenBase):
|
|||
"""
|
||||
return self._resize(canvwidth, canvheight, bg)
|
||||
|
||||
def save(self, filename, *, overwrite=False):
|
||||
"""Save the drawing as a PostScript file
|
||||
|
||||
Arguments:
|
||||
filename -- a string, the path of the created file.
|
||||
Must end with '.ps' or '.eps'.
|
||||
|
||||
Optional arguments:
|
||||
overwrite -- boolean, if true, then existing files will be overwritten
|
||||
|
||||
Example (for a TurtleScreen instance named screen):
|
||||
>>> screen.save('my_drawing.eps')
|
||||
"""
|
||||
filename = Path(filename)
|
||||
if not filename.parent.exists():
|
||||
raise FileNotFoundError(
|
||||
f"The directory '{filename.parent}' does not exist."
|
||||
" Cannot save to it."
|
||||
)
|
||||
if not overwrite and filename.exists():
|
||||
raise FileExistsError(
|
||||
f"The file '{filename}' already exists. To overwrite it use"
|
||||
" the 'overwrite=True' argument of the save function."
|
||||
)
|
||||
if (ext := filename.suffix) not in {".ps", ".eps"}:
|
||||
raise ValueError(
|
||||
f"Unknown file extension: '{ext}',"
|
||||
" must be one of {'.ps', '.eps'}"
|
||||
)
|
||||
|
||||
postscript = self.cv.postscript()
|
||||
filename.write_text(postscript)
|
||||
|
||||
onscreenclick = onclick
|
||||
resetscreen = reset
|
||||
clearscreen = clear
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Add :func:`turtle.save` to easily save Turtle drawings as PostScript files.
|
||||
Patch by Marie Roald and Yngve Mardal Moe.
|
Loading…
Reference in New Issue