Package: pathtyped
Typesafe resource management in PythonThis package creates a definition file by traversing the resource folder and parsing the filesFeaturesTypesafe resource management (needs compatible type checker)Automatic type definition generationMiddlewares, loaders apiPyPI:https://pypi.org/project/pathtyped/pipinstallpathtypedShowcaseThis code can be found indemoYour text filesresources/
data.json
main.txt
speech_0.txt
speech_1.txt
speech_2.txt```py
# main.py
from pathtyped import *

rm = ResourceManager(
    f"resources",
    DefinitionFile("", f"resource_definition.py"),
    [remove_known_extensions(r"txt|json"), group_by(r"(speech)_(\d)")],
    [Loaders.text, Loaders.json],
)

from resource_definition import root

resource: root = rm.root  # type: ignore
# The file below does not exist yet
from definition import root

resource: root = resource_manager.root # type: ignore# defaults.py# No need to import anything since type str is already imported# Examplefromsome_codeimportYourClassGenerates# 8587d74aed20f794cdfc800f26c456b7# Automatically generated by ResourceManager at: 2023-01-15 23:37:27.451164# DO NOT EDIT THIS FILE MANUALLY.# If you want to regenerate this file, change the integrity hash in the first line to something else# Some generic fixeslist=list[object]dict=dict[object,object]# Default import statement:# Main content:fromtypingimportNamedTupleroot=NamedTuple("root",[("speech",tuple[str,str,str,],),("data",dict),("main",str),])And provides you with type compilationDocumentationMiddlewaresMiddlewares are functions that have the ability to modify the tree structure itself as well as change the value of the nodesThey are applied in the order they are passed to the ResourceManager, with each middleware doing a full DFS preorder traversal of the treeformiddlewareinmiddlewares:# The @middleware decorator will initiate the dfs preorder traversaltree=middleware(self,"<root>",tree)This also means that the middleware will be applied to the leaf of the trees that it has returned. This is the most desirable behavior for applying middlewares@middlewaredefstrip_numbers_in_string(r:ResourceManager,location:str,tree:EntryTree)->Optional[EntryTree]:"""Removes all number property from tree"""ifisinstance(tree,EntryDict):new_tree=EntryDict()forkeyinlist(tree.keys()):n=re.sub(r"\d+","",key)new_tree[n]=tree[key]# If a new dict or list is returned, it will replace the old one# Be sure to return a new EntryTreereturnnew_tree# Returning None indicates that the tree should not be changed# You can also modify tree in place and it will be reflectedreturnNoneLoadersLoaders are simpler middlewares. They are functions that only take the leaf node and transform it into a useful value.Most leaf nodes arePathobjectsThe resource manager will attempt to apply each loader to the leaf node in the order they are passed to the ResourceManager. ReturningNoneindicates that the loader does not know how to handle the leaf node and will pass it to the next loaderforloaderinself.loaders:if(r:=loader(self,obj))isnotNone:returnrIf you want multiple loaders to be applied the same leaf node see the example code at the bottom# Basic loaders@loaderdefreturn_suffix(r:ResourceManager,path:Path)->str:returnpath.suffix@loader@extension("mp3")defpygame_load(r:ResourceManager,path:Path)->pygame.mixer.Sound:returnpygame.mixer.Sound(path)fallback_image=pygame.image.load("fallback.png")@loader@extension(r"png|jpg|jpeg")@fallback(fallback_image)# If the loading raises an Exceptiondefpygame_image_load(r:ResourceManager,path:Path)->pygame.Surface:returnpygame.image.load(path)# Advanced loaders# Loader that accepts certain object nodes# Requires a compatible middleware to generate such leaves@loader(Script)defexecute(r:ResourceManager,s:Script)->object:returns.execute()# Two transformations@loaderdefstr_node(r:ResourceManager,path:Path)->str:returnpath.read_text()@loader(str)defstr_to_int(r:ResourceManager,s:str)->int:ifs.isnumeric():returnint(s)@loaderdefcombined(r:ResourceManager,path:Path)->Union[str,int]:res=pathforlin[str_node,str_to_int]:if(t:=l(r,res))isnotNone:res=treturnresFAQVisual Studio Code shows that the type is anyChange thepython.analysis.typeCheckingModesetting tostrictand it not allow you to access invalid properties. Make sure that the Python language server is Pylance{"python.analysis.typeCheckingMode":"strict"}Cannot importTypeGuardProject requires Python 3.10 or higherContributingIf you have an idea for this library, please make an issue first instead of a PR. I will be happy to discuss it with you