Package: outspin
outspinoutspinis a tiny, low-abstraction library bringing C'sgetch()functionality to Python, with a sane API. An ideal choice for developers seeking
direct control over their TUI applications.InstallationExamplesSelect MenuTyping TestReferenceget_keywait_forpauseconstantsKnown IssuesContributingLicenseInstallationFrom PyPI:pipinstalloutspinFrom source:pipinstallgit+https://github.com/trag1c/outspin.gitExamplesSelect Menuhttps://github.com/trag1c/outspin/assets/77130613/ea0be955-302d-4ff3-85c9-8f7c451e6026Sourcefromoutspinimportwait_fordef_display_selected(*options:str,selected:int)->None:print("Select an option:")fori,optioninenumerate(options):print(f"{'>'ifi==selectedelse' '}{option}")print(f"\033[{len(options)+1}F",end="")defselect(*options:str)->str:selected=0_display_selected(*options,selected=selected)while(key:=wait_for("up","down","enter"))!="enter":selected+=1ifkey=="down"else-1selected%=len(options)_display_selected(*options,selected=selected)print("\n"*len(options))returnoptions[selected]print("Selected",select("Python","Rust","Swift","C++","C","Kotlin"))Typing Testhttps://github.com/trag1c/outspin/assets/77130613/c44a8d2f-8b1e-4948-8e78-15018e2e3667SourceRequiresdahliaandnouns.txtfrom__future__importannotationsimportsysfromcollections.abcimportIteratorfromdatetimeimportdatetimefromitertoolsimportcount,islice,zip_longestfrompathlibimportPathfromrandomimportchoicefromstringimportascii_lowercasefromdahliaimportdprintfromoutspinimportpause,wait_forNOUNS=[wforwinPath("nouns.txt").read_text().splitlines()iflen(w)<12andw.isalpha()]classWordQueue:def__init__(self)->None:self._gen=(choice(NOUNS)for_incount())self._queue:list[str]=[]self.load(4)defload(self,number:int=1)->None:self._queue.extend(islice(self._gen,number))@propertydefloaded(self)->tuple[str,...]:returntuple(self._queue)def__iter__(self)->Iterator[str]:returnselfdef__next__(self)->str:self._queue.pop(0)self.load()returnself._queue[0]defrender(wq:WordQueue,buffer:list[str])->None:current,*up_next=wq.loadedbuf_str="".join(buffer)first_bad_idx=((next(ifori,(a,b)inenumerate(zip_longest(buf_str,current,fillvalue="_"))ifa!=b)ifbuf_str!=currentelselen(current))ifbuf_strandcurrentelse0)dprint(f"\033[2F\033[0JUp next: &2{' '.join(up_next)}")print(f"\n>{buf_str[:first_bad_idx]}",end="")ifbad_content:=buf_str[first_bad_idx:]:dprint(f"&4{bad_content}&8{current[first_bad_idx+len(bad_content):]}",end="")else:dprint(f"&8{current[first_bad_idx:]}",end="")sys.stdout.flush()defmain(time:int)->None:pause()start_time=datetime.now()wq=WordQueue()buffer:list[str]=[]word=list(next(wq))typed_chars=0while(datetime.now()-start_time).seconds<time:render(wq,buffer)key=wait_for(*ascii_lowercase,"space","backspace")ifkey=="space":ifbuffer==word:buffer=[]typed_chars+=len(word)+1word=list(next(wq))elifkey=="backspace":ifbuffer:buffer.pop()else:buffer.append(key)print(f"\nWPM:{(typed_chars-1)/5/(time/60):.2f}")if__name__=="__main__":main(int(sys.argv[1]iflen(sys.argv)>1else30))Referenceget_keySignature:() -> NoneReturns a keypress from standard input. It exclusively identifies keypresses
that result in tangible inputs, therefore modifier keys likeshiftorcaps lockare ignored.outspinalso returns the actual input, meaning that
pressing, for instance,shift+awill makeget_key()returnA.[!Note]outspintranslates dozens of ANSI codes to human-readable names under the
hood. If you spot a case where an ANSI code (e.g.\x1b[15;2~) doesn't get
converted, please open an issue and/or submit a PR adding the code.wait_forSignature:(*keys: str) -> strWaits for one of the keys to be pressed and returns it.>>>wait_for(*"wasd")# pressing g t 4 2 q a'a'wait_forrequires at least one key to be provided.>>>wait_for()outspin.OutspinValueError: No keys to wait forpauseSignature:(prompt: str | None = None) -> NoneDisplays the prompt and pauses the program until a key is pressed.The default prompt isPress any key to continue....constantsA namespace containing a few useful characters groups:ARROWS:updownleftrightF_KEYS:f1→f12DIGITSorNUMBERS:0→9(same asstring.digits)LOWERCASE:a→z(same asstring.ascii_lowercase)UPPERCASE:A→Z(same asstring.ascii_uppercase)LETTERS:LOWERCASE+UPPERCASE(same asstring.ascii_letters)PUNCTUATION:!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~(same asstring.punctuation)Known IssuesSome combinations (likeshift+uporalt+shift+right) may not work
correctly on Windows.ContributingContributions are welcome!Please open an issue before submitting a pull request
(doesn't apply to minor changes like typos).To get started:Clone the project:gitclonehttps://github.com/trag1c/outspin.gitSet up the project withjust(make sure you havepoetryinstalled):justinstall[!Note]
If you don't want to installjust, simply look up the recipes
in the project'sjustfile.After you're done, use the followingjustrecipes to check your changes:justcheck# pytest, mypy, ruffjustcoverage# pytest (with coverage), interrogate (docstring coverage)Licenseoutspinis licensed under theMIT License.©trag1c, 2023–2024