From bd2da99347c9a38c8a1e9c835fdbe9361b5c0643 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler <49431240+abey79@users.noreply.github.com> Date: Wed, 17 Feb 2021 11:09:16 +0100 Subject: [PATCH] Use descriptor protocol for Param (#83) - Converted Param to use descriptor protocol - Added --destination to vsk save - Updated all examples - Added a test to run all examples - vsk commands now have exit status of 1 on error condition --- .github/pull_request_template.md | 2 +- .gitignore | 1 + examples/bezier_bugs/sketch_bezier_bugs.py | 8 +- examples/detail/sketch_detail.py | 4 +- examples/noise_bezier/sketch_noise_bezier.py | 6 +- .../prime_circles/sketch_prime_circles.py | 4 +- examples/quick_draw/sketch_quick_draw.py | 26 +++---- .../random_flower/sketch_random_flower.py | 8 +- examples/random_lines/sketch_random_lines.py | 6 +- examples/schotter/sketch_schotter.py | 10 +-- poetry.lock | 77 ++++++++++--------- tests/test_examples.py | 22 +++++- tests/utils.py | 2 +- vsketch/param.py | 5 +- vsketch_cli/cli.py | 39 +++++++--- 15 files changed, 127 insertions(+), 93 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 9036741..d06a44f 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -5,7 +5,7 @@ #### Checklist - [ ] feature/fix implemented -- [ ] `mypy vsketch tests` returns no error +- [ ] `mypy` returns no error - [ ] tests added/updated and `pytest --runslow` succeeds - [ ] documentation added/updated and building with no error (`make clean && make html` in `docs/`) - [ ] examples added/updated diff --git a/.gitignore b/.gitignore index 2a5ebac..01c70ac 100644 --- a/.gitignore +++ b/.gitignore @@ -134,3 +134,4 @@ dmypy.json /mywork/ /profile +.idea diff --git a/examples/bezier_bugs/sketch_bezier_bugs.py b/examples/bezier_bugs/sketch_bezier_bugs.py index 2e660d2..f38cfb4 100644 --- a/examples/bezier_bugs/sketch_bezier_bugs.py +++ b/examples/bezier_bugs/sketch_bezier_bugs.py @@ -42,10 +42,10 @@ class BezierBugSketch(vsketch.Vsketch): def draw(self) -> None: self.size("10in", "10in") - for row in range(self.row_count()): - for col in range(self.column_count()): - x = col * self.column_offset() - y = row * self.row_offset() + for row in range(self.row_count): + for col in range(self.column_count): + x = col * self.column_offset + y = row * self.row_offset bug(self, x, y) def finalize(self) -> None: diff --git a/examples/detail/sketch_detail.py b/examples/detail/sketch_detail.py index 2b23db6..48fbe61 100644 --- a/examples/detail/sketch_detail.py +++ b/examples/detail/sketch_detail.py @@ -2,13 +2,13 @@ class DetailSketch(vsketch.Vsketch): - detail_mm = vsketch.Param(1, 1, 50) + detail_value = vsketch.Param(1, 1, 50, unit="mm") def draw(self) -> None: self.size("a5", landscape=True) self.scale("1.5cm") - self.detail(f"{self.detail_mm()}mm") + self.detail(self.detail_value) self.circle(0, 0, 1) self.circle(0, 0, 2) diff --git a/examples/noise_bezier/sketch_noise_bezier.py b/examples/noise_bezier/sketch_noise_bezier.py index 038023a..a06e155 100644 --- a/examples/noise_bezier/sketch_noise_bezier.py +++ b/examples/noise_bezier/sketch_noise_bezier.py @@ -10,9 +10,9 @@ def draw(self) -> None: self.size("a4", landscape=False) self.scale("cm") - for i in range(self.N()): - t = i * self.freq() - v = i * self.drift() + for i in range(self.N): + t = i * self.freq + v = i * self.drift self.bezier( self.noise(t, 0) * 10 + v, self.noise(t, 1000) * 10 + v, diff --git a/examples/prime_circles/sketch_prime_circles.py b/examples/prime_circles/sketch_prime_circles.py index 096db2d..e61fe32 100644 --- a/examples/prime_circles/sketch_prime_circles.py +++ b/examples/prime_circles/sketch_prime_circles.py @@ -23,10 +23,10 @@ def draw(self) -> None: self.size("10in", "10in") self.scale("3mm") - for i, prime in enumerate(get_primes(self.N())): + for i, prime in enumerate(get_primes(self.N)): self.circle(0, 0, 2 * (i + 1)) - if self.random_phase(): + if self.random_phase: phase = np.random.random() * 2 * math.pi else: phase = -math.pi / 2 diff --git a/examples/quick_draw/sketch_quick_draw.py b/examples/quick_draw/sketch_quick_draw.py index 8db6308..055d5dd 100644 --- a/examples/quick_draw/sketch_quick_draw.py +++ b/examples/quick_draw/sketch_quick_draw.py @@ -417,11 +417,11 @@ class QuickDrawSketch(vsketch.Vsketch): scale_factor = vsketch.Param(3.0) def draw(self) -> None: - self.size(self.page_size(), landscape=self.landscape()) + self.size(self.page_size, landscape=self.landscape) self.penWidth("0.5mm") # obtain the datafile - file_name = self.category() + ".bin" + file_name = self.category + ".bin" file_path = pathlib.Path(file_name) url = "https://storage.googleapis.com/quickdraw_dataset/full/binary/" url += file_name.replace(" ", "%20") @@ -434,25 +434,23 @@ def draw(self) -> None: # draw stuff - width = self.width - 2 * self.margins() - height = self.height - 2 * self.margins() + width = self.width - 2 * self.margins + height = self.height - 2 * self.margins - n = self.columns() * self.rows() + n = self.columns * self.rows samples = random.sample(drawing_subset, n) - for j in range(self.rows()): + for j in range(self.rows): with self.pushMatrix(): - for i in range(self.columns()): - idx = j * self.columns() + i + for i in range(self.columns): + idx = j * self.columns + i with self.pushMatrix(): - self.scale( - self.scale_factor() * min(1 / self.columns(), 1 / self.rows()) - ) + self.scale(self.scale_factor * min(1 / self.columns, 1 / self.rows)) drawing = quickdraw_to_linestring(samples[idx]) - self.stroke((idx % self.layer_count()) + 1) + self.stroke((idx % self.layer_count) + 1) self.geometry(drawing) - self.translate(width / self.columns(), 0) + self.translate(width / self.columns, 0) - self.translate(0, height / self.rows()) + self.translate(0, height / self.rows) def finalize(self) -> None: self.vpype("linemerge linesort") diff --git a/examples/random_flower/sketch_random_flower.py b/examples/random_flower/sketch_random_flower.py index d860e47..de0b200 100644 --- a/examples/random_flower/sketch_random_flower.py +++ b/examples/random_flower/sketch_random_flower.py @@ -16,17 +16,17 @@ def draw(self) -> None: self.rotate(-90, degrees=True) - noise_coord = np.linspace(0, 1, self.point_per_line()) + noise_coord = np.linspace(0, 1, self.point_per_line) - dirs = np.linspace(0, 2 * math.pi, self.num_line()) + dirs = np.linspace(0, 2 * math.pi, self.num_line) for direction in dirs: rdir = self.map( np.array([self.noise(x, direction) for x in noise_coord]), 0, 1, - direction - self.rdir_range(), - direction + self.rdir_range(), + direction - self.rdir_range, + direction + self.rdir_range, ) roffset = self.map( np.array([self.noise(x, direction, 100) for x in noise_coord]), diff --git a/examples/random_lines/sketch_random_lines.py b/examples/random_lines/sketch_random_lines.py index e4105f8..573d190 100644 --- a/examples/random_lines/sketch_random_lines.py +++ b/examples/random_lines/sketch_random_lines.py @@ -15,15 +15,15 @@ def draw(self) -> None: x_coords = np.linspace(0, 25, 1000) - for i in range(self.num_line()): + for i in range(self.num_line): y_coords = ( np.array( [ - self.noise(x * self.x_freq(), i / self.num_line() * self.y_freq()) + self.noise(x * self.x_freq, i / self.num_line * self.y_freq) for x in x_coords ] ) - + self.y_offset() / self.num_line() * i + + self.y_offset / self.num_line * i ) self.polygon(x_coords, y_coords) diff --git a/examples/schotter/sketch_schotter.py b/examples/schotter/sketch_schotter.py index 9588c93..fb2d774 100644 --- a/examples/schotter/sketch_schotter.py +++ b/examples/schotter/sketch_schotter.py @@ -10,14 +10,14 @@ def draw(self) -> None: self.size("a4", landscape=False) self.scale("cm") - for j in range(self.rows()): + for j in range(self.rows): with self.pushMatrix(): - for i in range(self.columns()): + for i in range(self.columns): with self.pushMatrix(): - self.rotate(self.fuzziness() * 0.03 * self.random(-j, j)) + self.rotate(self.fuzziness * 0.03 * self.random(-j, j)) self.translate( - self.fuzziness() * 0.01 * self.randomGaussian() * j, - self.fuzziness() * 0.01 * self.randomGaussian() * j, + self.fuzziness * 0.01 * self.randomGaussian() * j, + self.fuzziness * 0.01 * self.randomGaussian() * j, ) self.rect(0, 0, 1, 1) self.translate(1, 0) diff --git a/poetry.lock b/poetry.lock index 6d67850..d251934 100644 --- a/poetry.lock +++ b/poetry.lock @@ -322,7 +322,7 @@ python-versions = ">=2.7" [[package]] name = "glcontext" -version = "2.3" +version = "2.3.2" description = "Portable OpenGL Context" category = "main" optional = false @@ -703,7 +703,7 @@ jupyterlab = ">=3.0,<4.0" [[package]] name = "jupyterlab-server" -version = "2.2.0" +version = "2.2.1" description = "JupyterLab Server" category = "main" optional = true @@ -1327,7 +1327,7 @@ python-versions = "*" [[package]] name = "sphinx" -version = "3.5.0" +version = "3.5.1" description = "Python documentation generator" category = "main" optional = true @@ -1352,9 +1352,9 @@ sphinxcontrib-qthelp = "*" sphinxcontrib-serializinghtml = "*" [package.extras] -lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.800)"] -test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"] docs = ["sphinxcontrib-websupport"] +lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.800)", "docutils-stubs"] +test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"] [[package]] name = "sphinx-autodoc-typehints" @@ -1582,11 +1582,11 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "vpype" -version = "1.5.0-alpha.0" +version = "1.6.0-alpha.0" description = "The Swiss Army knife of vector graphics for pen plotters" category = "main" optional = false -python-versions = ">=3.6.1, !=3.9.0, <3.10" +python-versions = ">=3.7, !=3.9.0, <3.10" develop = false [package.dependencies] @@ -1594,13 +1594,14 @@ attrs = "~20.3.0" cachetools = "^4.2.0" click = "~7.1.2" click-plugins = "~1.1.1" +glcontext = ">=2.3.2" matplotlib = "~3.3.2" moderngl = "^5.6.2" multiprocess = "^0.70.11" -numpy = {version = "^1.20.0", markers = "python_version >= \"3.7\""} +numpy = "^1.20" Pillow = "^8.1.0" PySide2 = "^5.15.2" -scipy = {version = "^1.6.0", markers = "python_version >= \"3.7\""} +scipy = "^1.6" Shapely = {version = "~1.7.1", extras = ["vectorized"]} svgelements = "1.4.7" svgwrite = "~1.4" @@ -1613,7 +1614,7 @@ docs = ["Sphinx (>=3.3.0,<4.0.0)", "sphinx-click (>=2.5.0,<3.0.0)", "sphinx-auto type = "git" url = "https://github.com/abey79/vpype" reference = "master" -resolved_reference = "fe8868b931328e3d765ff46916814c9f8f7d6469" +resolved_reference = "167486cb05377ccf73f096ffde97beeacec99cc6" [[package]] name = "watchgod" @@ -1854,30 +1855,30 @@ entrypoints = [ {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"}, ] glcontext = [ - {file = "glcontext-2.3-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:7895ed4f4aab4fccbd9dfe3c5eb5e85604ee7a9c0e7689995e82fdb76c4ac252"}, - {file = "glcontext-2.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:471d829a47292ff47724aede1ceba9a845bfc03f1e49c0dffa600d1e149529a5"}, - {file = "glcontext-2.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:ecb9fd614dc4384dcfad665d3e029ab68d068d635a4365f4645f008cc09c3a5e"}, - {file = "glcontext-2.3-cp35-cp35m-win32.whl", hash = "sha256:7293f012918b5a61defee8727ffb13cbdad3bbbeb44359c2caf2b8c7895af5ca"}, - {file = "glcontext-2.3-cp35-cp35m-win_amd64.whl", hash = "sha256:a0cb19f4a0c3b42451dd14883f85d6f22f2454e86d4f01cc76ad47f7f8a8c1be"}, - {file = "glcontext-2.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6003ea3044e6800cd6eb6f96973a038cdf45c1a423f3a89b40f87359ff6a7d5e"}, - {file = "glcontext-2.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:7b86b49f18949be1aee2e3732283a1a33814524d45cb8c5535e746fe3f3f3a1c"}, - {file = "glcontext-2.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe32359a63dac9fd7b13e80d62b7268308c5f35894991499f3d92ecec5775d63"}, - {file = "glcontext-2.3-cp36-cp36m-win32.whl", hash = "sha256:c161662bf347688a01c5788843a63e1e38390ad5c83014f9ea527cc52d43e7e5"}, - {file = "glcontext-2.3-cp36-cp36m-win_amd64.whl", hash = "sha256:f40a6aeb33b2f1c4c06bd70060425c1ace76360bd07d020df66bbd8d91dbee00"}, - {file = "glcontext-2.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:55727b569aca2f665c9f1348ef3c709ec655dcab0d90ae71caa92843f62a315f"}, - {file = "glcontext-2.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6e3fdca6c3986bf614aa6727f1f17f6e82c5809d7b859c604345bb00a6b8b22d"}, - {file = "glcontext-2.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ab1bfa3f3bdb13e393de1b2a953256ab298aeeddd1b5fb1ba9c8270819d856eb"}, - {file = "glcontext-2.3-cp37-cp37m-win32.whl", hash = "sha256:fbba930c259ab858f27ebd58b3bca501badd2b324923f2c6e3e8450724205e5b"}, - {file = "glcontext-2.3-cp37-cp37m-win_amd64.whl", hash = "sha256:a19ef8b6cb466d4d383ca689bb75100c22708c79f18f5584e9c0f8d2b2bb064f"}, - {file = "glcontext-2.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a1c8192fd6641ce9ee383fa9582e43f5e9f81931693be40bcffa77f1560b1ac5"}, - {file = "glcontext-2.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:12df95ea141bb607295cf760d084d694950a1ebf96c9aa246231867bf1cd3fca"}, - {file = "glcontext-2.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:e63b997e9a5d6fe18b9a64b0e90a540bbdf8b06dbebe693feecbadc0d579537a"}, - {file = "glcontext-2.3-cp38-cp38-win32.whl", hash = "sha256:38d37df2efeb194948ebcc79875faf2f0ae8af88cb65d91a7dc3051a5e60e9da"}, - {file = "glcontext-2.3-cp38-cp38-win_amd64.whl", hash = "sha256:ff84c7559a7c427ea5f7c94409d16bd47bc6d35a133c7c386b62d393ad984295"}, - {file = "glcontext-2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e50c41c421c541b4c2b499620d7f917ebe1c5424aba5eaf07e48593ab024ae61"}, - {file = "glcontext-2.3-cp39-cp39-win32.whl", hash = "sha256:508e21edad75cb2c4a5ca5892b9fc99e1843944f0867d42012fe38a6c1fb0550"}, - {file = "glcontext-2.3-cp39-cp39-win_amd64.whl", hash = "sha256:170ad25921f3911a46d8f295274d6261aaf9bb8e86f862b15af5d85c34912eea"}, - {file = "glcontext-2.3.tar.gz", hash = "sha256:35b9576dfd0c97ddea238c89d7fc0d9f0721485898d0d5995cfc80df4be6df91"}, + {file = "glcontext-2.3.2-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:8b92a3bcca1cd0be5fa6add8c3feb723747a160b372523c6f720485c7648838d"}, + {file = "glcontext-2.3.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:3c62af1f972f97565deac6d08230bcd3ce09e6ab580b822eebe30dd9c902d19b"}, + {file = "glcontext-2.3.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:771c36d0a2fc03b94130f46ff996e0f607c72b54fcba72fb7493cf2dd18fe46a"}, + {file = "glcontext-2.3.2-cp35-cp35m-win32.whl", hash = "sha256:fdf3ae07f8de29b7fa9c6006b5bc6b8fc8e508455574405473f680ecba8272b7"}, + {file = "glcontext-2.3.2-cp35-cp35m-win_amd64.whl", hash = "sha256:eb35d324239cc42df402b8716f0aa0d7f30d68b895f183f4d4a3f424b3b9579b"}, + {file = "glcontext-2.3.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:17cf58e4a70897da8a99d72044cf1437e2fd232d4e3af599e018e858e27df6d7"}, + {file = "glcontext-2.3.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:470474a7b2e963af284c07116dd9b3f7fab0a5195da308f33599fcad1ef5de7f"}, + {file = "glcontext-2.3.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:842492c39ec7fc87f982300def79751dab7888b985c6f1dab13b5937f56f5a0d"}, + {file = "glcontext-2.3.2-cp36-cp36m-win32.whl", hash = "sha256:b59ec600fbdd7bffeb22ac96fd1639980fe08a41a5935c9deb0c2012cdd190da"}, + {file = "glcontext-2.3.2-cp36-cp36m-win_amd64.whl", hash = "sha256:4385d2d6626a7f3d33b06eefe5b506bb1c4afc2b2996b9ac8c5ff32d7de2b432"}, + {file = "glcontext-2.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ecea09f612eed473b5eec2f56b68e3278ce683e7220b2895e5864b6c4ccef87f"}, + {file = "glcontext-2.3.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:59bf670a5899998ee7190bc5130da025512c765b36b89dcb06477535f67ae882"}, + {file = "glcontext-2.3.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:eb83051e4246160c1f60943b74accb5fe91835022deb753ffc18e3087d82a011"}, + {file = "glcontext-2.3.2-cp37-cp37m-win32.whl", hash = "sha256:6257b0319bf05859630c117550d46b71973b591e863bd8b1eb6faec0f51199fe"}, + {file = "glcontext-2.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:7804bc3ef37ba0d35845e02e5e428713857b181bd2826ffc48b863613e40649b"}, + {file = "glcontext-2.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eae3ff044064f2b6b5a6023afbccd6751aa6b1c1896dd4bb411e36cc8f64c143"}, + {file = "glcontext-2.3.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:01632bc0c33e71f57801daacb0f3e552a01a426e1e9cd45dadd969bc66fc0c9c"}, + {file = "glcontext-2.3.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:e34b8d6f0598d27ce02fb1b535d37827b37259ddc811ec689091d68e9f8df054"}, + {file = "glcontext-2.3.2-cp38-cp38-win32.whl", hash = "sha256:beeb8aa834773cc3dc1c37809841cfb26be16dfd2dedffdf76a246fb9c92de51"}, + {file = "glcontext-2.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:dad729ebbb870a725aba7fba75589d3b90e1b495b8d4b4533721b8ea798898f8"}, + {file = "glcontext-2.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54324b5b5486ca53f83a64410939c08549ee5253845675131b54e57c8f9bc11a"}, + {file = "glcontext-2.3.2-cp39-cp39-win32.whl", hash = "sha256:44f8c5a07add0b90a46b10e3f328731f51d7ac973047dfada6e0338d3bf3f77e"}, + {file = "glcontext-2.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:950bdbfad1162ecaee03cdd070ab02b0e0ce4a9efe03708f706bd3c5985f29f7"}, + {file = "glcontext-2.3.2.tar.gz", hash = "sha256:f716c05689ddf911afe68c7e7e3ac2b283e40a184031d81055141d310f07235e"}, ] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, @@ -1976,8 +1977,8 @@ jupyterlab-code-formatter = [ {file = "jupyterlab_code_formatter-1.4.4.tar.gz", hash = "sha256:f97081098953c4ead2a45af94ab6d42146ccf951a4b8cec6cd26f25a7238b8e5"}, ] jupyterlab-server = [ - {file = "jupyterlab_server-2.2.0-py3-none-any.whl", hash = "sha256:8bb220b1926a418c80869337f79096cae487ac579f304f574b970ed37c84e31c"}, - {file = "jupyterlab_server-2.2.0.tar.gz", hash = "sha256:68b9322eee2561c89a22fcdf755c57fd750e8132f18fda81d47ca336d1f9b066"}, + {file = "jupyterlab_server-2.2.1-py3-none-any.whl", hash = "sha256:b647c532bbb45dad83b1dcd18e9b8cd9a22a68f799811cbca35616b3e8d27acc"}, + {file = "jupyterlab_server-2.2.1.tar.gz", hash = "sha256:8b619ec5e13c2d1ac2e3a43a8147382f41fb17b425b50fa38b1cc84849c7bf94"}, ] jupyterlab-widgets = [ {file = "jupyterlab_widgets-1.0.0-py3-none-any.whl", hash = "sha256:caeaf3e6103180e654e7d8d2b81b7d645e59e432487c1d35a41d6d3ee56b3fef"}, @@ -2532,8 +2533,8 @@ snowballstemmer = [ {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] sphinx = [ - {file = "Sphinx-3.5.0-py3-none-any.whl", hash = "sha256:68da66ca3d6b35b22bea5c53d938d5f8988663dca042f0a46429a1eba1010051"}, - {file = "Sphinx-3.5.0.tar.gz", hash = "sha256:deb468efb3abaa70d790add4147d18782d86fdeacf648d6e8afb7a99807f1546"}, + {file = "Sphinx-3.5.1-py3-none-any.whl", hash = "sha256:e90161222e4d80ce5fc811ace7c6787a226b4f5951545f7f42acf97277bfc35c"}, + {file = "Sphinx-3.5.1.tar.gz", hash = "sha256:11d521e787d9372c289472513d807277caafb1684b33eb4f08f7574c405893a9"}, ] sphinx-autodoc-typehints = [ {file = "sphinx-autodoc-typehints-1.11.1.tar.gz", hash = "sha256:244ba6d3e2fdb854622f643c7763d6f95b6886eba24bec28e86edf205e4ddb20"}, diff --git a/tests/test_examples.py b/tests/test_examples.py index 9bccb4e..32e00d5 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1,17 +1,31 @@ -import glob import pathlib import nbformat import pytest from nbconvert.preprocessors import ExecutePreprocessor +from typer.testing import CliRunner +from vsketch_cli.cli import cli -EXAMPLES = str(pathlib.Path(__file__).parent.parent.absolute()) + "/examples/_notebooks/" +EXAMPLES = pathlib.Path(__file__).parent.parent.absolute() / "examples" +NOTEBOOKS = EXAMPLES / "_notebooks" + +runner = CliRunner() @pytest.mark.slow -@pytest.mark.parametrize("ipynb", glob.glob(EXAMPLES + "*.ipynb")) -def test_example_detail(tmp_path, ipynb): +@pytest.mark.parametrize("ipynb", NOTEBOOKS.glob("*.ipynb")) +def test_example_notebooks(tmp_path, ipynb): with open(ipynb) as f: nb = nbformat.read(f, as_version=4) ep = ExecutePreprocessor(timeout=600, kernel_name="python3") ep.preprocess(nb, {"metadata": {"path": tmp_path}}) + + +@pytest.mark.parametrize("path", EXAMPLES.glob("[!._]*[!.md]")) +def test_examples(tmp_path, path): + # Note: split is needed to avoid `invoke`'s preprocessor to eat Windows' path backslashes + res = runner.invoke( + cli, f"save --name test_output --destination {tmp_path} {str(path)}".split() + ) + assert res.exit_code == 0 + assert (pathlib.Path(tmp_path) / "test_output.svg").exists() diff --git a/tests/utils.py b/tests/utils.py index 368810d..2bf2d82 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -11,7 +11,7 @@ def bounds_equal( """Asserts that sketch bounds are approximately equal to those provided""" bounds = vsk.document.bounds() - return ( + return bool( bounds is not None and np.isclose(bounds[0], xmin, rtol=1e-03) and np.isclose(bounds[1], ymin, rtol=1e-03) diff --git a/vsketch/param.py b/vsketch/param.py index d5a3750..a13566f 100644 --- a/vsketch/param.py +++ b/vsketch/param.py @@ -101,7 +101,10 @@ def set_value_with_validation(self, v: Any) -> None: self.value = value - def __call__(self) -> ParamType: + def __get__(self, instance: Any, owner: Any = None) -> Any: + if instance is None: + return self + if self.factor is None: return self.value else: diff --git a/vsketch_cli/cli.py b/vsketch_cli/cli.py index 48f61bf..f7ada3f 100644 --- a/vsketch_cli/cli.py +++ b/vsketch_cli/cli.py @@ -194,7 +194,7 @@ def run( path = _find_sketch_script(target) except ValueError as err: print_error("Sketch could not be found: ", str(err)) - return + raise typer.Exit(code=1) print_info("Running sketch: ", str(path)) @@ -220,6 +220,9 @@ def save( ), ), seed: Optional[str] = typer.Option(None, "-s", "--seed", help="seed or seed range to use"), + destination: Optional[str] = typer.Option( + None, "-d", "--destination", help="destination path" + ), multiprocessing: bool = typer.Option( True, envvar="VSK_MULTIPROCESSING", help="enable multiprocessing" ), @@ -242,13 +245,16 @@ def save( If the number of files to generate is greater than 4, all available cores are used for the process. This behaviour can be disabled with --no-multiprocessing or the VSK_MULTIPROCESSING variable. + + By default, all SVG are saved in the sketch's "output" sub-directory. This can be + overridden using the --destination option. """ try: path = _find_sketch_script(target) except ValueError as err: print_error("Sketch could not be found: ", str(err)) - return + raise typer.Exit(code=1) # load configuration param_set: Dict[str, vsketch.ParamType] = {} @@ -279,15 +285,26 @@ def save( seed_start, seed_end = _parse_seed(seed) except ValueError as err: print_error(f"Could not parse seed {seed}: ", str(err)) - return + raise typer.Exit(code=1) # prepare output path - output_path = path.parent / "output" - if not output_path.exists(): - output_path.mkdir() - elif not output_path.is_dir(): - print_error("Could not create output directory: ", str(output_path)) - return + if destination is not None: + output_path = pathlib.Path(destination) + if not output_path.exists(): + print_error("Provided output path does not exist: ", str(output_path.absolute())) + raise typer.Exit(code=1) + if not output_path.is_dir(): + print_error( + "Provided output path is not a directory: ", str(output_path.absolute()) + ) + raise typer.Exit(code=1) + else: + output_path = path.parent / "output" + if not output_path.exists(): + output_path.mkdir() + elif not output_path.is_dir(): + print_error("Could not create output directory: ", str(output_path)) + raise typer.Exit(code=1) # noinspection PyShadowingNames def _write_output(seed: int) -> None: @@ -295,7 +312,7 @@ def _write_output(seed: int) -> None: sketch_class = load_sketch_class(path) if sketch_class is None: print_error("Could not load script: ", str(path)) - return + raise typer.Exit(code=1) sketch_class.set_param_set(param_set) @@ -310,7 +327,7 @@ def _write_output(seed: int) -> None: if vsk is None: print_error("Could not execute script: ", str(path)) - return + raise typer.Exit(code=1) doc = vsk.document with open(output_file, "w") as fp: