|
5 | 5 |
|
6 | 6 | import collections |
7 | 7 | import itertools |
8 | | -import math |
9 | | -import os |
10 | | -from numbers import Integral, Real, Complex, Number |
11 | | -from collections.abc import Iterable |
12 | 8 | import json |
| 9 | +import math |
13 | 10 | import os |
14 | 11 | import pickle |
15 | 12 | import re |
| 13 | +import shlex |
16 | 14 | import sys |
17 | 15 | import tempfile |
18 | 16 | import textwrap |
19 | 17 | import typing |
20 | | -import shlex |
| 18 | +import urllib.parse |
21 | 19 | from abc import ABC, abstractmethod |
22 | 20 | from collections.abc import Iterable |
| 21 | +from numbers import Complex, Integral, Number, Real |
23 | 22 | from pathlib import Path |
24 | | -from typing import List, Optional, Pattern, Tuple, Union, Dict |
| 23 | +from typing import Dict, List, Optional, Pattern, Tuple, TypeVar, Union |
25 | 24 | from urllib.error import URLError |
26 | | -import urllib.parse |
27 | | -from typing import TypeVar |
28 | 25 |
|
29 | 26 | from snakemake import io as io_ |
30 | 27 | from snakemake import sourcecache |
31 | | -from snakemake.common import MIN_PY_VERSION, ON_WINDOWS, get_snakemake_searchpaths |
| 28 | +from snakemake.common import ( |
| 29 | + MIN_PY_VERSION, |
| 30 | + ON_WINDOWS, |
| 31 | + get_report_id, |
| 32 | + get_snakemake_searchpaths, |
| 33 | +) |
32 | 34 | from snakemake.deployment import singularity |
33 | 35 | from snakemake.exceptions import WorkflowError |
34 | 36 | from snakemake.logging import logger |
|
40 | 42 | infer_source_file, |
41 | 43 | ) |
42 | 44 | from snakemake.utils import format |
43 | | -from snakemake.common import get_report_id |
44 | 45 |
|
45 | 46 | # TODO use this to find the right place for inserting the preamble |
46 | 47 | PY_PREAMBLE_RE = re.compile(r"from( )+__future__( )+import.*?(?P<end>[;\n])") |
@@ -1616,6 +1617,15 @@ def execute_script(self, fname, edit=False): |
1616 | 1617 | ) |
1617 | 1618 |
|
1618 | 1619 |
|
| 1620 | +class HyScript(PythonScript): |
| 1621 | + def write_script(self, preamble, fd): |
| 1622 | + fd.write(f"(pys #[[{preamble}]])".encode()) |
| 1623 | + fd.write(self.source.encode()) |
| 1624 | + |
| 1625 | + def execute_script(self, fname, edit=False): |
| 1626 | + self._execute_cmd("hy {fname:q}", fname=fname) |
| 1627 | + |
| 1628 | + |
1619 | 1629 | def strip_re(regex: Pattern, s: str) -> Tuple[str, str]: |
1620 | 1630 | """Strip a substring matching a regex from a string and return the stripped part |
1621 | 1631 | and the remainder of the original string. |
@@ -1680,6 +1690,8 @@ def get_language(source_file, source): |
1680 | 1690 | language = "bash" |
1681 | 1691 | elif filename.endswith(".xsh"): |
1682 | 1692 | language = "xonsh" |
| 1693 | + elif filename.endswith(".hy"): |
| 1694 | + language = "hy" |
1683 | 1695 |
|
1684 | 1696 | # detect kernel language for Jupyter Notebooks |
1685 | 1697 | if language == "jupyter": |
@@ -1744,10 +1756,11 @@ def script( |
1744 | 1756 | "rust": RustScript, |
1745 | 1757 | "bash": BashScript, |
1746 | 1758 | "xonsh": XonshScript, |
| 1759 | + "hy": HyScript, |
1747 | 1760 | }.get(language, None) |
1748 | 1761 | if exec_class is None: |
1749 | 1762 | raise ValueError( |
1750 | | - "Unsupported script: Expecting either Python (.py), R (.R), RMarkdown (.Rmd), Julia (.jl), Rust (.rs), Bash (.sh), or Xonsh (.xsh) script." |
| 1763 | + "Script must be one of the following filetypes: [.py .R .Rmd .jl .rs .sh .xsh .hy]" |
1751 | 1764 | ) |
1752 | 1765 |
|
1753 | 1766 | executor = exec_class( |
|
0 commit comments