this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

✨ Fix some more bugs, handle in-between sigils, add tests

+356 -28
+1 -1
README.md
··· 33 33 34 34 Indentation is done with tab characters only. 35 35 36 - - **Title:** The title is made up of any word in the line that does not start with `~`, `@` or `%`. 36 + - **Title:** The title is made up of any word in the line that does not start with `~`, `@` or `%`. Words that start with any of these symbols will not be added to the title, except if they are in the middle (in that case, they both get added as tags/assignees/milestones and as a word in the title, without the prefix symbol) 37 37 - **Tags:** Prefix a word with `~` to add a label to the issue 38 38 - **Assignees:** Prefix with `@` to add an assignee. The special assignee `@me` is supported. 39 39 - **Milestone:** Prefix with `%` to set the milestone
-4
issurge/main.py
··· 10 10 --dry-run Don't actually post the issues 11 11 --debug Print debug information 12 12 """ 13 - import json 14 13 import os 15 - from collections.abc import Iterable 16 14 from pathlib import Path 17 - from subprocess import run 18 - from typing import Generator, TypeAlias, TypeVar 19 15 20 16 from docopt import docopt 21 17 from rich import print
+75 -21
issurge/parser.py
··· 50 50 51 51 52 52 class Issue(NamedTuple): 53 - title: str 54 - description: str 55 - labels: set[str] 56 - assignees: set[str] 57 - milestone: str 53 + title: str = "" 54 + description: str = "" 55 + labels: set[str] = set() 56 + assignees: set[str] = set() 57 + milestone: str = "" 58 + 59 + def __rich_repr__(self): 60 + yield self.title 61 + yield "description", self.description, "" 62 + yield "labels", self.labels, set() 63 + yield "assignees", self.assignees, set() 64 + yield "milestone", self.milestone, "" 58 65 59 66 def __str__(self) -> str: 67 + result = f"{self.title}" or "<No title>" 68 + if self.labels: 69 + result += f" {' '.join(['~' + l for l in self.labels])}" 70 + if self.milestone: 71 + result += f" %{self.milestone}" 72 + if self.assignees: 73 + result += f" {' '.join(['@' + a for a in self.assignees])}" 74 + if self.description: 75 + result += f": {self.description}" 76 + return result 77 + 78 + def display(self) -> str: 60 79 result = f"[white]{self.title[:30]}[/white]" or "[red]<No title>[/red]" 61 80 if len(self.title) > 30: 62 81 result += " [white dim](...)[/white dim]" ··· 74 93 result += " [white][...][/white]" 75 94 return result 76 95 77 - def submit(self): 78 - remote_url = urlparse( 79 - subprocess.run(["git", "remote", "get-url", "origin"]).stdout.decode() 80 - ) 96 + def submit(self, submitter_args: list[str]): 97 + remote_url = self._get_remote_url() 81 98 if remote_url.hostname == "github.com": 82 99 self._github_submit(submitter_args) 83 100 else: 84 101 self._gitlab_submit(submitter_args) 85 102 103 + def _get_remote_url(self): 104 + try: 105 + return urlparse( 106 + subprocess.run( 107 + ["git", "remote", "get-url", "origin"], capture_output=True 108 + ).stdout.decode() 109 + ) 110 + except subprocess.CalledProcessError as e: 111 + raise ValueError( 112 + "Could not determine remote url, make sure that you are inside of a git repository that has a remote named 'origin'" 113 + ) from e 114 + 86 115 def _gitlab_submit(self, submitter_args: list[str]): 87 116 command = ["glab", "issue", "new"] 88 117 if self.title: ··· 101 130 command = ["gh", "issue", "new"] 102 131 if self.title: 103 132 command += ["-t", self.title] 104 - command += ["-d", self.description or ""] 133 + command += ["-b", self.description or ""] 105 134 for a in self.assignees: 106 135 command += ["-a", a if a != "me" else "@me"] 107 136 for l in self.labels: ··· 124 153 f"Calling [white bold]{e.cmd}[/] failed with code [white bold]{e.returncode}[/]:\n{NEWLINE.join(TAB + line for line in e.stderr.decode().splitlines())}" 125 154 ) 126 155 156 + @staticmethod 157 + def _word_and_sigil(raw_word: str) -> tuple[str, str]: 158 + sigil = raw_word[0] 159 + word = raw_word[1:] 160 + if sigil not in ("~", "%", "@"): 161 + sigil = "" 162 + word = raw_word 163 + return sigil, word 164 + 127 165 # The boolean is true if the issue expects a description (ending ':') 128 166 @classmethod 129 167 def parse(cls, raw: str) -> tuple["Issue", bool]: ··· 138 176 labels = set() 139 177 assignees = set() 140 178 milestone = "" 141 - for word in raw.split(" "): 142 - if word.startswith("~"): 143 - labels.add(word[1:]) 144 - elif word.startswith("%"): 145 - milestone = word[1:] 146 - elif word.startswith("@"): 147 - assignees.add(word[1:]) 148 - else: 179 + # only labels/milestones/assignees at the beginning or end of the line are not added to the title as words 180 + add_to_title = False 181 + remaining_words = [word.strip() for word in raw.split(" ") if word.strip()] 182 + 183 + while remaining_words: 184 + sigil, word = cls._word_and_sigil(remaining_words.pop(0)) 185 + 186 + if sigil and add_to_title: 149 187 title += f" {word}" 188 + 189 + match sigil: 190 + case "~": 191 + labels.add(word) 192 + case "%": 193 + milestone = word 194 + case "@": 195 + assignees.add(word) 196 + case _: 197 + title += f" {word}" 198 + # add to title if there are remaining regular words 199 + add_to_title = any( 200 + not sigil 201 + for (sigil, _) in map(cls._word_and_sigil, remaining_words) 202 + ) 150 203 151 204 return ( 152 205 cls( ··· 212 265 ) 213 266 214 267 if current_issue.title: 215 - log(f"Made {current_issue!s}") 268 + log(f"Made {current_issue.display()}") 216 269 return [current_issue] 217 270 218 271 if not expecting_description and children is not None: 219 272 result = [] 220 - log(f"Making children from {current_issue!s}") 273 + log(f"Making children from {current_issue.display()}") 221 274 for child, grandchildren in children.items(): 222 275 result.extend( 223 276 parse_issue_fragment( ··· 236 289 237 290 def parse(raw: str) -> Iterable[Issue]: 238 291 for item in Node.to_dict(raw).items(): 239 - yield parse_issue_fragment(*item, Issue("", "", set(), set(), "")) 292 + for issue in parse_issue_fragment(*item, Issue("", "", set(), set(), "")): 293 + yield issue
+38
issurge/parser_test.py
··· 1 + from ward import test 2 + from .parser import Issue 3 + 4 + for fragment, expected, description_expected in [ 5 + ("", Issue(), False), 6 + ("a simple test right there", Issue(title="a simple test right there"), False), 7 + ( 8 + "@me some ~labels to ~organize issues ~bug", 9 + Issue( 10 + title="some labels to organize issues", 11 + labels={"labels", "organize", "bug"}, 12 + assignees={"me"}, 13 + ), 14 + False, 15 + ), 16 + ( 17 + "a %milestone to keep ~track of stuff", 18 + Issue( 19 + title="a milestone to keep track of stuff", 20 + labels={"track"}, 21 + milestone="milestone", 22 + ), 23 + False, 24 + ), 25 + ( 26 + "A label with a description following it ~now:", 27 + Issue(title="A label with a description following it", labels={"now"}), 28 + True, 29 + ), 30 + ]: 31 + 32 + @test(f"parse {fragment!r}") 33 + def _( 34 + fragment=fragment, expected=expected, description_expected=description_expected 35 + ): 36 + actual, expecting_description = Issue.parse(fragment) 37 + assert expecting_description == description_expected 38 + assert actual == expected
+1
issurge/utils.py
··· 13 13 14 14 15 15 TAB = "\t" 16 + NEWLINE = "\n"
+238 -2
poetry.lock
··· 1 - # This file is automatically @generated by Poetry and should not be changed by hand. 1 + # This file is automatically @generated by Poetry 1.4.1 and should not be changed by hand. 2 + 3 + [[package]] 4 + name = "click" 5 + version = "8.1.3" 6 + description = "Composable command line interface toolkit" 7 + category = "dev" 8 + optional = false 9 + python-versions = ">=3.7" 10 + files = [ 11 + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, 12 + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, 13 + ] 14 + 15 + [package.dependencies] 16 + colorama = {version = "*", markers = "platform_system == \"Windows\""} 17 + 18 + [[package]] 19 + name = "click-completion" 20 + version = "0.5.2" 21 + description = "Fish, Bash, Zsh and PowerShell completion for Click" 22 + category = "dev" 23 + optional = false 24 + python-versions = "*" 25 + files = [ 26 + {file = "click-completion-0.5.2.tar.gz", hash = "sha256:5bf816b81367e638a190b6e91b50779007d14301b3f9f3145d68e3cade7bce86"}, 27 + ] 28 + 29 + [package.dependencies] 30 + click = "*" 31 + jinja2 = "*" 32 + shellingham = "*" 33 + six = "*" 34 + 35 + [[package]] 36 + name = "click-default-group" 37 + version = "1.2.2" 38 + description = "Extends click.Group to invoke a command without explicit subcommand name" 39 + category = "dev" 40 + optional = false 41 + python-versions = "*" 42 + files = [ 43 + {file = "click-default-group-1.2.2.tar.gz", hash = "sha256:d9560e8e8dfa44b3562fbc9425042a0fd6d21956fcc2db0077f63f34253ab904"}, 44 + ] 45 + 46 + [package.dependencies] 47 + click = "*" 48 + 49 + [[package]] 50 + name = "colorama" 51 + version = "0.4.6" 52 + description = "Cross-platform colored terminal text." 53 + category = "dev" 54 + optional = false 55 + python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 56 + files = [ 57 + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 58 + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 59 + ] 60 + 61 + [[package]] 62 + name = "cucumber-tag-expressions" 63 + version = "4.1.0" 64 + description = "Provides tag-expression parser for cucumber/behave" 65 + category = "dev" 66 + optional = false 67 + python-versions = ">=2.7" 68 + files = [ 69 + {file = "cucumber-tag-expressions-4.1.0.tar.gz", hash = "sha256:e314d5fed6eebb2f90380271f562248fb15e18636764faf40f4dde4b28b1f960"}, 70 + ] 71 + 72 + [package.extras] 73 + develop = ["coverage", "invoke", "path.py", "pylint", "pytest (>=3.2)", "pytest-html (>=1.19.0)", "tox (>=2.9)"] 2 74 3 75 [[package]] 4 76 name = "docopt" ··· 12 84 ] 13 85 14 86 [[package]] 87 + name = "jinja2" 88 + version = "3.1.2" 89 + description = "A very fast and expressive template engine." 90 + category = "dev" 91 + optional = false 92 + python-versions = ">=3.7" 93 + files = [ 94 + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, 95 + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, 96 + ] 97 + 98 + [package.dependencies] 99 + MarkupSafe = ">=2.0" 100 + 101 + [package.extras] 102 + i18n = ["Babel (>=2.7)"] 103 + 104 + [[package]] 15 105 name = "markdown-it-py" 16 106 version = "2.2.0" 17 107 description = "Python port of markdown-it. Markdown parsing, done right!" ··· 37 127 testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] 38 128 39 129 [[package]] 130 + name = "markupsafe" 131 + version = "2.1.2" 132 + description = "Safely add untrusted strings to HTML/XML markup." 133 + category = "dev" 134 + optional = false 135 + python-versions = ">=3.7" 136 + files = [ 137 + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, 138 + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, 139 + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, 140 + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, 141 + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, 142 + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, 143 + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, 144 + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, 145 + {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, 146 + {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, 147 + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, 148 + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, 149 + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, 150 + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, 151 + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, 152 + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, 153 + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, 154 + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, 155 + {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, 156 + {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, 157 + {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, 158 + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, 159 + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, 160 + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, 161 + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, 162 + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, 163 + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, 164 + {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, 165 + {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, 166 + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, 167 + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, 168 + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, 169 + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, 170 + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, 171 + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, 172 + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, 173 + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, 174 + {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, 175 + {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, 176 + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, 177 + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, 178 + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, 179 + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, 180 + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, 181 + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, 182 + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, 183 + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, 184 + {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, 185 + {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, 186 + {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, 187 + ] 188 + 189 + [[package]] 40 190 name = "mdurl" 41 191 version = "0.1.2" 42 192 description = "Markdown URL utilities" ··· 46 196 files = [ 47 197 {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, 48 198 {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, 199 + ] 200 + 201 + [[package]] 202 + name = "pluggy" 203 + version = "1.0.0" 204 + description = "plugin and hook calling mechanisms for python" 205 + category = "dev" 206 + optional = false 207 + python-versions = ">=3.6" 208 + files = [ 209 + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, 210 + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, 211 + ] 212 + 213 + [package.extras] 214 + dev = ["pre-commit", "tox"] 215 + testing = ["pytest", "pytest-benchmark"] 216 + 217 + [[package]] 218 + name = "pprintpp" 219 + version = "0.4.0" 220 + description = "A drop-in replacement for pprint that's actually pretty" 221 + category = "dev" 222 + optional = false 223 + python-versions = "*" 224 + files = [ 225 + {file = "pprintpp-0.4.0-py2.py3-none-any.whl", hash = "sha256:b6b4dcdd0c0c0d75e4d7b2f21a9e933e5b2ce62b26e1a54537f9651ae5a5c01d"}, 226 + {file = "pprintpp-0.4.0.tar.gz", hash = "sha256:ea826108e2c7f49dc6d66c752973c3fc9749142a798d6b254e1e301cfdbc6403"}, 49 227 ] 50 228 51 229 [[package]] ··· 82 260 [package.extras] 83 261 jupyter = ["ipywidgets (>=7.5.1,<9)"] 84 262 263 + [[package]] 264 + name = "shellingham" 265 + version = "1.5.0.post1" 266 + description = "Tool to Detect Surrounding Shell" 267 + category = "dev" 268 + optional = false 269 + python-versions = ">=3.7" 270 + files = [ 271 + {file = "shellingham-1.5.0.post1-py2.py3-none-any.whl", hash = "sha256:368bf8c00754fd4f55afb7bbb86e272df77e4dc76ac29dbcbb81a59e9fc15744"}, 272 + {file = "shellingham-1.5.0.post1.tar.gz", hash = "sha256:823bc5fb5c34d60f285b624e7264f4dda254bc803a3774a147bf99c0e3004a28"}, 273 + ] 274 + 275 + [[package]] 276 + name = "six" 277 + version = "1.16.0" 278 + description = "Python 2 and 3 compatibility utilities" 279 + category = "dev" 280 + optional = false 281 + python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 282 + files = [ 283 + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 284 + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 285 + ] 286 + 287 + [[package]] 288 + name = "tomli" 289 + version = "2.0.1" 290 + description = "A lil' TOML parser" 291 + category = "dev" 292 + optional = false 293 + python-versions = ">=3.7" 294 + files = [ 295 + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 296 + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 297 + ] 298 + 299 + [[package]] 300 + name = "ward" 301 + version = "0.67.2b0" 302 + description = "A modern Python testing framework" 303 + category = "dev" 304 + optional = false 305 + python-versions = ">=3.7.8,<4.0.0" 306 + files = [ 307 + {file = "ward-0.67.2b0-py3-none-any.whl", hash = "sha256:fe351f5ae2fbbf8132af6260bdea8245dcef026943cb2084faaa48e6a1017c4e"}, 308 + {file = "ward-0.67.2b0.tar.gz", hash = "sha256:11bf0128e4696bfd1ca9a2b457c817378c4479a33fa45d0e3aabf7b301744f47"}, 309 + ] 310 + 311 + [package.dependencies] 312 + click = ">=7,<9" 313 + click-completion = ">=0.5.2,<0.6.0" 314 + click-default-group = ">=1.2.2,<2.0.0" 315 + cucumber-tag-expressions = ">=2.0.0,<5.0.0" 316 + pluggy = ">=0.13.1,<2.0.0" 317 + pprintpp = ">=0.4.0,<0.5.0" 318 + rich = ">=12.2.0" 319 + tomli = ">=1.0.0,<3.0.0" 320 + 85 321 [metadata] 86 322 lock-version = "2.0" 87 323 python-versions = "^3.10" 88 - content-hash = "e9cb0bf1a5b56042fca97b6ee7080855364a369ff6a81b56c1c912cb4d49ab4c" 324 + content-hash = "60f31c4e79148be98bc29ad6db9a4f27090b21b0d58da04b479b188600c89cef"
+3
pyproject.toml
··· 13 13 docopt = "^0.6.2" 14 14 15 15 16 + [tool.poetry.group.dev.dependencies] 17 + ward = "^0.67.2b0" 18 + 16 19 [build-system] 17 20 requires = ["poetry-core"] 18 21 build-backend = "poetry.core.masonry.api"